Roo/bootstrap/Calendar.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         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
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         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 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
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @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'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @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'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @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'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 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).
4848 <p>
4849 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>
4850
4851 <p>
4852 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.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <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>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <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>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * 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;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @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).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * 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)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * 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).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * 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).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * 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).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * 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).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * 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).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
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         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * 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.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <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
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @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}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @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.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 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)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * 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.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * 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)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @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:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * 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().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     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>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @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
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @return {CompositeElement} this
10950     */
10951     filter : function(selector){
10952         var els = [];
10953         this.each(function(el){
10954             if(el.is(selector)){
10955                 els[els.length] = el.dom;
10956             }
10957         });
10958         this.fill(els);
10959         return this;
10960     },
10961
10962     invoke : function(fn, args){
10963         var els = this.elements;
10964         for(var i = 0, len = els.length; i < len; i++) {
10965                 Roo.Element.prototype[fn].apply(els[i], args);
10966         }
10967         return this;
10968     },
10969     /**
10970     * Adds elements to this composite.
10971     * @param {String/Array} els A string CSS selector, an array of elements or an element
10972     * @return {CompositeElement} this
10973     */
10974     add : function(els){
10975         if(typeof els == "string"){
10976             this.addElements(Roo.Element.selectorFunction(els));
10977         }else if(els.length !== undefined){
10978             this.addElements(els);
10979         }else{
10980             this.addElements([els]);
10981         }
10982         return this;
10983     },
10984     /**
10985     * Calls the passed function passing (el, this, index) for each element in this composite.
10986     * @param {Function} fn The function to call
10987     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10988     * @return {CompositeElement} this
10989     */
10990     each : function(fn, scope){
10991         var els = this.elements;
10992         for(var i = 0, len = els.length; i < len; i++){
10993             if(fn.call(scope || els[i], els[i], this, i) === false) {
10994                 break;
10995             }
10996         }
10997         return this;
10998     },
10999
11000     /**
11001      * Returns the Element object at the specified index
11002      * @param {Number} index
11003      * @return {Roo.Element}
11004      */
11005     item : function(index){
11006         return this.elements[index] || null;
11007     },
11008
11009     /**
11010      * Returns the first Element
11011      * @return {Roo.Element}
11012      */
11013     first : function(){
11014         return this.item(0);
11015     },
11016
11017     /**
11018      * Returns the last Element
11019      * @return {Roo.Element}
11020      */
11021     last : function(){
11022         return this.item(this.elements.length-1);
11023     },
11024
11025     /**
11026      * Returns the number of elements in this composite
11027      * @return Number
11028      */
11029     getCount : function(){
11030         return this.elements.length;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     contains : function(el){
11038         return this.indexOf(el) !== -1;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     indexOf : function(el){
11046         return this.elements.indexOf(Roo.get(el));
11047     },
11048
11049
11050     /**
11051     * Removes the specified element(s).
11052     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11053     * or an array of any of those.
11054     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11055     * @return {CompositeElement} this
11056     */
11057     removeElement : function(el, removeDom){
11058         if(el instanceof Array){
11059             for(var i = 0, len = el.length; i < len; i++){
11060                 this.removeElement(el[i]);
11061             }
11062             return this;
11063         }
11064         var index = typeof el == 'number' ? el : this.indexOf(el);
11065         if(index !== -1){
11066             if(removeDom){
11067                 var d = this.elements[index];
11068                 if(d.dom){
11069                     d.remove();
11070                 }else{
11071                     d.parentNode.removeChild(d);
11072                 }
11073             }
11074             this.elements.splice(index, 1);
11075         }
11076         return this;
11077     },
11078
11079     /**
11080     * Replaces the specified element with the passed element.
11081     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11082     * to replace.
11083     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11084     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11085     * @return {CompositeElement} this
11086     */
11087     replaceElement : function(el, replacement, domReplace){
11088         var index = typeof el == 'number' ? el : this.indexOf(el);
11089         if(index !== -1){
11090             if(domReplace){
11091                 this.elements[index].replaceWith(replacement);
11092             }else{
11093                 this.elements.splice(index, 1, Roo.get(replacement))
11094             }
11095         }
11096         return this;
11097     },
11098
11099     /**
11100      * Removes all elements.
11101      */
11102     clear : function(){
11103         this.elements = [];
11104     }
11105 };
11106 (function(){
11107     Roo.CompositeElement.createCall = function(proto, fnName){
11108         if(!proto[fnName]){
11109             proto[fnName] = function(){
11110                 return this.invoke(fnName, arguments);
11111             };
11112         }
11113     };
11114     for(var fnName in Roo.Element.prototype){
11115         if(typeof Roo.Element.prototype[fnName] == "function"){
11116             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11117         }
11118     };
11119 })();
11120 /*
11121  * Based on:
11122  * Ext JS Library 1.1.1
11123  * Copyright(c) 2006-2007, Ext JS, LLC.
11124  *
11125  * Originally Released Under LGPL - original licence link has changed is not relivant.
11126  *
11127  * Fork - LGPL
11128  * <script type="text/javascript">
11129  */
11130
11131 /**
11132  * @class Roo.CompositeElementLite
11133  * @extends Roo.CompositeElement
11134  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11135  <pre><code>
11136  var els = Roo.select("#some-el div.some-class");
11137  // or select directly from an existing element
11138  var el = Roo.get('some-el');
11139  el.select('div.some-class');
11140
11141  els.setWidth(100); // all elements become 100 width
11142  els.hide(true); // all elements fade out and hide
11143  // or
11144  els.setWidth(100).hide(true);
11145  </code></pre><br><br>
11146  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11147  * actions will be performed on all the elements in this collection.</b>
11148  */
11149 Roo.CompositeElementLite = function(els){
11150     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11151     this.el = new Roo.Element.Flyweight();
11152 };
11153 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11154     addElements : function(els){
11155         if(els){
11156             if(els instanceof Array){
11157                 this.elements = this.elements.concat(els);
11158             }else{
11159                 var yels = this.elements;
11160                 var index = yels.length-1;
11161                 for(var i = 0, len = els.length; i < len; i++) {
11162                     yels[++index] = els[i];
11163                 }
11164             }
11165         }
11166         return this;
11167     },
11168     invoke : function(fn, args){
11169         var els = this.elements;
11170         var el = this.el;
11171         for(var i = 0, len = els.length; i < len; i++) {
11172             el.dom = els[i];
11173                 Roo.Element.prototype[fn].apply(el, args);
11174         }
11175         return this;
11176     },
11177     /**
11178      * Returns a flyweight Element of the dom element object at the specified index
11179      * @param {Number} index
11180      * @return {Roo.Element}
11181      */
11182     item : function(index){
11183         if(!this.elements[index]){
11184             return null;
11185         }
11186         this.el.dom = this.elements[index];
11187         return this.el;
11188     },
11189
11190     // fixes scope with flyweight
11191     addListener : function(eventName, handler, scope, opt){
11192         var els = this.elements;
11193         for(var i = 0, len = els.length; i < len; i++) {
11194             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11195         }
11196         return this;
11197     },
11198
11199     /**
11200     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11201     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11202     * a reference to the dom node, use el.dom.</b>
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         var el = this.el;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             el.dom = els[i];
11212                 if(fn.call(scope || el, el, this, i) === false){
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.getDom(el));
11221     },
11222
11223     replaceElement : function(el, replacement, domReplace){
11224         var index = typeof el == 'number' ? el : this.indexOf(el);
11225         if(index !== -1){
11226             replacement = Roo.getDom(replacement);
11227             if(domReplace){
11228                 var d = this.elements[index];
11229                 d.parentNode.insertBefore(replacement, d);
11230                 d.parentNode.removeChild(d);
11231             }
11232             this.elements.splice(index, 1, replacement);
11233         }
11234         return this;
11235     }
11236 });
11237 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11238
11239 /*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250  
11251
11252 /**
11253  * @class Roo.data.Connection
11254  * @extends Roo.util.Observable
11255  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11256  * either to a configured URL, or to a URL specified at request time.<br><br>
11257  * <p>
11258  * Requests made by this class are asynchronous, and will return immediately. No data from
11259  * the server will be available to the statement immediately following the {@link #request} call.
11260  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11261  * <p>
11262  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11263  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11264  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11265  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11266  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11267  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11268  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11269  * standard DOM methods.
11270  * @constructor
11271  * @param {Object} config a configuration object.
11272  */
11273 Roo.data.Connection = function(config){
11274     Roo.apply(this, config);
11275     this.addEvents({
11276         /**
11277          * @event beforerequest
11278          * Fires before a network request is made to retrieve a data object.
11279          * @param {Connection} conn This Connection object.
11280          * @param {Object} options The options config object passed to the {@link #request} method.
11281          */
11282         "beforerequest" : true,
11283         /**
11284          * @event requestcomplete
11285          * Fires if the request was successfully completed.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestcomplete" : true,
11292         /**
11293          * @event requestexception
11294          * Fires if an error HTTP status was returned from the server.
11295          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11296          * @param {Connection} conn This Connection object.
11297          * @param {Object} response The XHR object containing the response data.
11298          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11299          * @param {Object} options The options config object passed to the {@link #request} method.
11300          */
11301         "requestexception" : true
11302     });
11303     Roo.data.Connection.superclass.constructor.call(this);
11304 };
11305
11306 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11307     /**
11308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11312      * extra parameters to each request made by this object. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11316      *  to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @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)
11320      */
11321     /**
11322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11323      */
11324     timeout : 30000,
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11327      * @type Boolean
11328      */
11329     autoAbort:false,
11330
11331     /**
11332      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11333      * @type Boolean
11334      */
11335     disableCaching: true,
11336
11337     /**
11338      * Sends an HTTP request to a remote server.
11339      * @param {Object} options An object which may contain the following properties:<ul>
11340      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11341      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11342      * request, a url encoded string or a function to call to get either.</li>
11343      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11344      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11345      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11346      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11347      * <li>options {Object} The parameter to the request call.</li>
11348      * <li>success {Boolean} True if the request succeeded.</li>
11349      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11350      * </ul></li>
11351      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11352      * The callback is passed the following parameters:<ul>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * <li>options {Object} The parameter to the request call.</li>
11355      * </ul></li>
11356      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11357      * The callback is passed the following parameters:<ul>
11358      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11359      * <li>options {Object} The parameter to the request call.</li>
11360      * </ul></li>
11361      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11362      * for the callback function. Defaults to the browser window.</li>
11363      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11364      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11365      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11366      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11367      * params for the post data. Any params will be appended to the URL.</li>
11368      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11369      * </ul>
11370      * @return {Number} transactionId
11371      */
11372     request : function(o){
11373         if(this.fireEvent("beforerequest", this, o) !== false){
11374             var p = o.params;
11375
11376             if(typeof p == "function"){
11377                 p = p.call(o.scope||window, o);
11378             }
11379             if(typeof p == "object"){
11380                 p = Roo.urlEncode(o.params);
11381             }
11382             if(this.extraParams){
11383                 var extras = Roo.urlEncode(this.extraParams);
11384                 p = p ? (p + '&' + extras) : extras;
11385             }
11386
11387             var url = o.url || this.url;
11388             if(typeof url == 'function'){
11389                 url = url.call(o.scope||window, o);
11390             }
11391
11392             if(o.form){
11393                 var form = Roo.getDom(o.form);
11394                 url = url || form.action;
11395
11396                 var enctype = form.getAttribute("enctype");
11397                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11398                     return this.doFormUpload(o, p, url);
11399                 }
11400                 var f = Roo.lib.Ajax.serializeForm(form);
11401                 p = p ? (p + '&' + f) : f;
11402             }
11403
11404             var hs = o.headers;
11405             if(this.defaultHeaders){
11406                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11407                 if(!o.headers){
11408                     o.headers = hs;
11409                 }
11410             }
11411
11412             var cb = {
11413                 success: this.handleResponse,
11414                 failure: this.handleFailure,
11415                 scope: this,
11416                 argument: {options: o},
11417                 timeout : o.timeout || this.timeout
11418             };
11419
11420             var method = o.method||this.method||(p ? "POST" : "GET");
11421
11422             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11423                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11424             }
11425
11426             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11427                 if(o.autoAbort){
11428                     this.abort();
11429                 }
11430             }else if(this.autoAbort !== false){
11431                 this.abort();
11432             }
11433
11434             if((method == 'GET' && p) || o.xmlData){
11435                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11436                 p = '';
11437             }
11438             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11439             return this.transId;
11440         }else{
11441             Roo.callback(o.callback, o.scope, [o, null, null]);
11442             return null;
11443         }
11444     },
11445
11446     /**
11447      * Determine whether this object has a request outstanding.
11448      * @param {Number} transactionId (Optional) defaults to the last transaction
11449      * @return {Boolean} True if there is an outstanding request.
11450      */
11451     isLoading : function(transId){
11452         if(transId){
11453             return Roo.lib.Ajax.isCallInProgress(transId);
11454         }else{
11455             return this.transId ? true : false;
11456         }
11457     },
11458
11459     /**
11460      * Aborts any outstanding request.
11461      * @param {Number} transactionId (Optional) defaults to the last transaction
11462      */
11463     abort : function(transId){
11464         if(transId || this.isLoading()){
11465             Roo.lib.Ajax.abort(transId || this.transId);
11466         }
11467     },
11468
11469     // private
11470     handleResponse : function(response){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestcomplete", this, response, options);
11475         Roo.callback(options.success, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, true, response]);
11477     },
11478
11479     // private
11480     handleFailure : function(response, e){
11481         this.transId = false;
11482         var options = response.argument.options;
11483         response.argument = options ? options.argument : null;
11484         this.fireEvent("requestexception", this, response, options, e);
11485         Roo.callback(options.failure, options.scope, [response, options]);
11486         Roo.callback(options.callback, options.scope, [options, false, response]);
11487     },
11488
11489     // private
11490     doFormUpload : function(o, ps, url){
11491         var id = Roo.id();
11492         var frame = document.createElement('iframe');
11493         frame.id = id;
11494         frame.name = id;
11495         frame.className = 'x-hidden';
11496         if(Roo.isIE){
11497             frame.src = Roo.SSL_SECURE_URL;
11498         }
11499         document.body.appendChild(frame);
11500
11501         if(Roo.isIE){
11502            document.frames[id].name = id;
11503         }
11504
11505         var form = Roo.getDom(o.form);
11506         form.target = id;
11507         form.method = 'POST';
11508         form.enctype = form.encoding = 'multipart/form-data';
11509         if(url){
11510             form.action = url;
11511         }
11512
11513         var hiddens, hd;
11514         if(ps){ // add dynamic params
11515             hiddens = [];
11516             ps = Roo.urlDecode(ps, false);
11517             for(var k in ps){
11518                 if(ps.hasOwnProperty(k)){
11519                     hd = document.createElement('input');
11520                     hd.type = 'hidden';
11521                     hd.name = k;
11522                     hd.value = ps[k];
11523                     form.appendChild(hd);
11524                     hiddens.push(hd);
11525                 }
11526             }
11527         }
11528
11529         function cb(){
11530             var r = {  // bogus response object
11531                 responseText : '',
11532                 responseXML : null
11533             };
11534
11535             r.argument = o ? o.argument : null;
11536
11537             try { //
11538                 var doc;
11539                 if(Roo.isIE){
11540                     doc = frame.contentWindow.document;
11541                 }else {
11542                     doc = (frame.contentDocument || window.frames[id].document);
11543                 }
11544                 if(doc && doc.body){
11545                     r.responseText = doc.body.innerHTML;
11546                 }
11547                 if(doc && doc.XMLDocument){
11548                     r.responseXML = doc.XMLDocument;
11549                 }else {
11550                     r.responseXML = doc;
11551                 }
11552             }
11553             catch(e) {
11554                 // ignore
11555             }
11556
11557             Roo.EventManager.removeListener(frame, 'load', cb, this);
11558
11559             this.fireEvent("requestcomplete", this, r, o);
11560             Roo.callback(o.success, o.scope, [r, o]);
11561             Roo.callback(o.callback, o.scope, [o, true, r]);
11562
11563             setTimeout(function(){document.body.removeChild(frame);}, 100);
11564         }
11565
11566         Roo.EventManager.on(frame, 'load', cb, this);
11567         form.submit();
11568
11569         if(hiddens){ // remove dynamic params
11570             for(var i = 0, len = hiddens.length; i < len; i++){
11571                 form.removeChild(hiddens[i]);
11572             }
11573         }
11574     }
11575 });
11576 /*
11577  * Based on:
11578  * Ext JS Library 1.1.1
11579  * Copyright(c) 2006-2007, Ext JS, LLC.
11580  *
11581  * Originally Released Under LGPL - original licence link has changed is not relivant.
11582  *
11583  * Fork - LGPL
11584  * <script type="text/javascript">
11585  */
11586  
11587 /**
11588  * Global Ajax request class.
11589  * 
11590  * @class Roo.Ajax
11591  * @extends Roo.data.Connection
11592  * @static
11593  * 
11594  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11595  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11596  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11597  * @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)
11598  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11599  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11600  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11601  */
11602 Roo.Ajax = new Roo.data.Connection({
11603     // fix up the docs
11604     /**
11605      * @scope Roo.Ajax
11606      * @type {Boolear} 
11607      */
11608     autoAbort : false,
11609
11610     /**
11611      * Serialize the passed form into a url encoded string
11612      * @scope Roo.Ajax
11613      * @param {String/HTMLElement} form
11614      * @return {String}
11615      */
11616     serializeForm : function(form){
11617         return Roo.lib.Ajax.serializeForm(form);
11618     }
11619 });/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630  
11631 /**
11632  * @class Roo.UpdateManager
11633  * @extends Roo.util.Observable
11634  * Provides AJAX-style update for Element object.<br><br>
11635  * Usage:<br>
11636  * <pre><code>
11637  * // Get it from a Roo.Element object
11638  * var el = Roo.get("foo");
11639  * var mgr = el.getUpdateManager();
11640  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11641  * ...
11642  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11643  * <br>
11644  * // or directly (returns the same UpdateManager instance)
11645  * var mgr = new Roo.UpdateManager("myElementId");
11646  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11647  * mgr.on("update", myFcnNeedsToKnow);
11648  * <br>
11649    // short handed call directly from the element object
11650    Roo.get("foo").load({
11651         url: "bar.php",
11652         scripts:true,
11653         params: "for=bar",
11654         text: "Loading Foo..."
11655    });
11656  * </code></pre>
11657  * @constructor
11658  * Create new UpdateManager directly.
11659  * @param {String/HTMLElement/Roo.Element} el The element to update
11660  * @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).
11661  */
11662 Roo.UpdateManager = function(el, forceNew){
11663     el = Roo.get(el);
11664     if(!forceNew && el.updateManager){
11665         return el.updateManager;
11666     }
11667     /**
11668      * The Element object
11669      * @type Roo.Element
11670      */
11671     this.el = el;
11672     /**
11673      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11674      * @type String
11675      */
11676     this.defaultUrl = null;
11677
11678     this.addEvents({
11679         /**
11680          * @event beforeupdate
11681          * Fired before an update is made, return false from your handler and the update is cancelled.
11682          * @param {Roo.Element} el
11683          * @param {String/Object/Function} url
11684          * @param {String/Object} params
11685          */
11686         "beforeupdate": true,
11687         /**
11688          * @event update
11689          * Fired after successful update is made.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "update": true,
11694         /**
11695          * @event failure
11696          * Fired on update failure.
11697          * @param {Roo.Element} el
11698          * @param {Object} oResponseObject The response Object
11699          */
11700         "failure": true
11701     });
11702     var d = Roo.UpdateManager.defaults;
11703     /**
11704      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11705      * @type String
11706      */
11707     this.sslBlankUrl = d.sslBlankUrl;
11708     /**
11709      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11710      * @type Boolean
11711      */
11712     this.disableCaching = d.disableCaching;
11713     /**
11714      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11715      * @type String
11716      */
11717     this.indicatorText = d.indicatorText;
11718     /**
11719      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11720      * @type String
11721      */
11722     this.showLoadIndicator = d.showLoadIndicator;
11723     /**
11724      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11725      * @type Number
11726      */
11727     this.timeout = d.timeout;
11728
11729     /**
11730      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11731      * @type Boolean
11732      */
11733     this.loadScripts = d.loadScripts;
11734
11735     /**
11736      * Transaction object of current executing transaction
11737      */
11738     this.transaction = null;
11739
11740     /**
11741      * @private
11742      */
11743     this.autoRefreshProcId = null;
11744     /**
11745      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.refreshDelegate = this.refresh.createDelegate(this);
11749     /**
11750      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11751      * @type Function
11752      */
11753     this.updateDelegate = this.update.createDelegate(this);
11754     /**
11755      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11756      * @type Function
11757      */
11758     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11759     /**
11760      * @private
11761      */
11762     this.successDelegate = this.processSuccess.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.failureDelegate = this.processFailure.createDelegate(this);
11767
11768     if(!this.renderer){
11769      /**
11770       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11771       */
11772     this.renderer = new Roo.UpdateManager.BasicRenderer();
11773     }
11774     
11775     Roo.UpdateManager.superclass.constructor.call(this);
11776 };
11777
11778 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11779     /**
11780      * Get the Element this UpdateManager is bound to
11781      * @return {Roo.Element} The element
11782      */
11783     getEl : function(){
11784         return this.el;
11785     },
11786     /**
11787      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11788      * @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:
11789 <pre><code>
11790 um.update({<br/>
11791     url: "your-url.php",<br/>
11792     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11793     callback: yourFunction,<br/>
11794     scope: yourObject, //(optional scope)  <br/>
11795     discardUrl: false, <br/>
11796     nocache: false,<br/>
11797     text: "Loading...",<br/>
11798     timeout: 30,<br/>
11799     scripts: false<br/>
11800 });
11801 </code></pre>
11802      * The only required property is url. The optional properties nocache, text and scripts
11803      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11804      * @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}
11805      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11806      * @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.
11807      */
11808     update : function(url, params, callback, discardUrl){
11809         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11810             var method = this.method,
11811                 cfg;
11812             if(typeof url == "object"){ // must be config object
11813                 cfg = url;
11814                 url = cfg.url;
11815                 params = params || cfg.params;
11816                 callback = callback || cfg.callback;
11817                 discardUrl = discardUrl || cfg.discardUrl;
11818                 if(callback && cfg.scope){
11819                     callback = callback.createDelegate(cfg.scope);
11820                 }
11821                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11822                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11823                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11824                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11825                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11826             }
11827             this.showLoading();
11828             if(!discardUrl){
11829                 this.defaultUrl = url;
11830             }
11831             if(typeof url == "function"){
11832                 url = url.call(this);
11833             }
11834
11835             method = method || (params ? "POST" : "GET");
11836             if(method == "GET"){
11837                 url = this.prepareUrl(url);
11838             }
11839
11840             var o = Roo.apply(cfg ||{}, {
11841                 url : url,
11842                 params: params,
11843                 success: this.successDelegate,
11844                 failure: this.failureDelegate,
11845                 callback: undefined,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11848             });
11849             Roo.log("updated manager called with timeout of " + o.timeout);
11850             this.transaction = Roo.Ajax.request(o);
11851         }
11852     },
11853
11854     /**
11855      * 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.
11856      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11857      * @param {String/HTMLElement} form The form Id or form element
11858      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11859      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11861      */
11862     formUpdate : function(form, url, reset, callback){
11863         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11864             if(typeof url == "function"){
11865                 url = url.call(this);
11866             }
11867             form = Roo.getDom(form);
11868             this.transaction = Roo.Ajax.request({
11869                 form: form,
11870                 url:url,
11871                 success: this.successDelegate,
11872                 failure: this.failureDelegate,
11873                 timeout: (this.timeout*1000),
11874                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11875             });
11876             this.showLoading.defer(1, this);
11877         }
11878     },
11879
11880     /**
11881      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11883      */
11884     refresh : function(callback){
11885         if(this.defaultUrl == null){
11886             return;
11887         }
11888         this.update(this.defaultUrl, null, callback, true);
11889     },
11890
11891     /**
11892      * Set this element to auto refresh.
11893      * @param {Number} interval How often to update (in seconds).
11894      * @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)
11895      * @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}
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11898      */
11899     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11900         if(refreshNow){
11901             this.update(url || this.defaultUrl, params, callback, true);
11902         }
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905         }
11906         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11907     },
11908
11909     /**
11910      * Stop auto refresh on this element.
11911      */
11912      stopAutoRefresh : function(){
11913         if(this.autoRefreshProcId){
11914             clearInterval(this.autoRefreshProcId);
11915             delete this.autoRefreshProcId;
11916         }
11917     },
11918
11919     isAutoRefreshing : function(){
11920        return this.autoRefreshProcId ? true : false;
11921     },
11922     /**
11923      * Called to update the element to "Loading" state. Override to perform custom action.
11924      */
11925     showLoading : function(){
11926         if(this.showLoadIndicator){
11927             this.el.update(this.indicatorText);
11928         }
11929     },
11930
11931     /**
11932      * Adds unique parameter to query string if disableCaching = true
11933      * @private
11934      */
11935     prepareUrl : function(url){
11936         if(this.disableCaching){
11937             var append = "_dc=" + (new Date().getTime());
11938             if(url.indexOf("?") !== -1){
11939                 url += "&" + append;
11940             }else{
11941                 url += "?" + append;
11942             }
11943         }
11944         return url;
11945     },
11946
11947     /**
11948      * @private
11949      */
11950     processSuccess : function(response){
11951         this.transaction = null;
11952         if(response.argument.form && response.argument.reset){
11953             try{ // put in try/catch since some older FF releases had problems with this
11954                 response.argument.form.reset();
11955             }catch(e){}
11956         }
11957         if(this.loadScripts){
11958             this.renderer.render(this.el, response, this,
11959                 this.updateComplete.createDelegate(this, [response]));
11960         }else{
11961             this.renderer.render(this.el, response, this);
11962             this.updateComplete(response);
11963         }
11964     },
11965
11966     updateComplete : function(response){
11967         this.fireEvent("update", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, true, response);
11970         }
11971     },
11972
11973     /**
11974      * @private
11975      */
11976     processFailure : function(response){
11977         this.transaction = null;
11978         this.fireEvent("failure", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, false, response);
11981         }
11982     },
11983
11984     /**
11985      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11986      * @param {Object} renderer The object implementing the render() method
11987      */
11988     setRenderer : function(renderer){
11989         this.renderer = renderer;
11990     },
11991
11992     getRenderer : function(){
11993        return this.renderer;
11994     },
11995
11996     /**
11997      * Set the defaultUrl used for updates
11998      * @param {String/Function} defaultUrl The url or a function to call to get the url
11999      */
12000     setDefaultUrl : function(defaultUrl){
12001         this.defaultUrl = defaultUrl;
12002     },
12003
12004     /**
12005      * Aborts the executing transaction
12006      */
12007     abort : function(){
12008         if(this.transaction){
12009             Roo.Ajax.abort(this.transaction);
12010         }
12011     },
12012
12013     /**
12014      * Returns true if an update is in progress
12015      * @return {Boolean}
12016      */
12017     isUpdating : function(){
12018         if(this.transaction){
12019             return Roo.Ajax.isLoading(this.transaction);
12020         }
12021         return false;
12022     }
12023 });
12024
12025 /**
12026  * @class Roo.UpdateManager.defaults
12027  * @static (not really - but it helps the doc tool)
12028  * The defaults collection enables customizing the default properties of UpdateManager
12029  */
12030    Roo.UpdateManager.defaults = {
12031        /**
12032          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12033          * @type Number
12034          */
12035          timeout : 30,
12036
12037          /**
12038          * True to process scripts by default (Defaults to false).
12039          * @type Boolean
12040          */
12041         loadScripts : false,
12042
12043         /**
12044         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12045         * @type String
12046         */
12047         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12048         /**
12049          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12050          * @type Boolean
12051          */
12052         disableCaching : false,
12053         /**
12054          * Whether to show indicatorText when loading (Defaults to true).
12055          * @type Boolean
12056          */
12057         showLoadIndicator : true,
12058         /**
12059          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12060          * @type String
12061          */
12062         indicatorText : '<div class="loading-indicator">Loading...</div>'
12063    };
12064
12065 /**
12066  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12067  *Usage:
12068  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12069  * @param {String/HTMLElement/Roo.Element} el The element to update
12070  * @param {String} url The url
12071  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12072  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12073  * @static
12074  * @deprecated
12075  * @member Roo.UpdateManager
12076  */
12077 Roo.UpdateManager.updateElement = function(el, url, params, options){
12078     var um = Roo.get(el, true).getUpdateManager();
12079     Roo.apply(um, options);
12080     um.update(url, params, options ? options.callback : null);
12081 };
12082 // alias for backwards compat
12083 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12084 /**
12085  * @class Roo.UpdateManager.BasicRenderer
12086  * Default Content renderer. Updates the elements innerHTML with the responseText.
12087  */
12088 Roo.UpdateManager.BasicRenderer = function(){};
12089
12090 Roo.UpdateManager.BasicRenderer.prototype = {
12091     /**
12092      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12093      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12094      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12095      * @param {Roo.Element} el The element being rendered
12096      * @param {Object} response The YUI Connect response object
12097      * @param {UpdateManager} updateManager The calling update manager
12098      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12099      */
12100      render : function(el, response, updateManager, callback){
12101         el.update(response.responseText, updateManager.loadScripts, callback);
12102     }
12103 };
12104 /*
12105  * Based on:
12106  * Roo JS
12107  * (c)) Alan Knowles
12108  * Licence : LGPL
12109  */
12110
12111
12112 /**
12113  * @class Roo.DomTemplate
12114  * @extends Roo.Template
12115  * An effort at a dom based template engine..
12116  *
12117  * Similar to XTemplate, except it uses dom parsing to create the template..
12118  *
12119  * Supported features:
12120  *
12121  *  Tags:
12122
12123 <pre><code>
12124       {a_variable} - output encoded.
12125       {a_variable.format:("Y-m-d")} - call a method on the variable
12126       {a_variable:raw} - unencoded output
12127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12128       {a_variable:this.method_on_template(...)} - call a method on the template object.
12129  
12130 </code></pre>
12131  *  The tpl tag:
12132 <pre><code>
12133         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12134         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12135         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12136         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12137   
12138 </code></pre>
12139  *      
12140  */
12141 Roo.DomTemplate = function()
12142 {
12143      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12144      if (this.html) {
12145         this.compile();
12146      }
12147 };
12148
12149
12150 Roo.extend(Roo.DomTemplate, Roo.Template, {
12151     /**
12152      * id counter for sub templates.
12153      */
12154     id : 0,
12155     /**
12156      * flag to indicate if dom parser is inside a pre,
12157      * it will strip whitespace if not.
12158      */
12159     inPre : false,
12160     
12161     /**
12162      * The various sub templates
12163      */
12164     tpls : false,
12165     
12166     
12167     
12168     /**
12169      *
12170      * basic tag replacing syntax
12171      * WORD:WORD()
12172      *
12173      * // you can fake an object call by doing this
12174      *  x.t:(test,tesT) 
12175      * 
12176      */
12177     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12178     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12179     
12180     iterChild : function (node, method) {
12181         
12182         var oldPre = this.inPre;
12183         if (node.tagName == 'PRE') {
12184             this.inPre = true;
12185         }
12186         for( var i = 0; i < node.childNodes.length; i++) {
12187             method.call(this, node.childNodes[i]);
12188         }
12189         this.inPre = oldPre;
12190     },
12191     
12192     
12193     
12194     /**
12195      * compile the template
12196      *
12197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12198      *
12199      */
12200     compile: function()
12201     {
12202         var s = this.html;
12203         
12204         // covert the html into DOM...
12205         var doc = false;
12206         var div =false;
12207         try {
12208             doc = document.implementation.createHTMLDocument("");
12209             doc.documentElement.innerHTML =   this.html  ;
12210             div = doc.documentElement;
12211         } catch (e) {
12212             // old IE... - nasty -- it causes all sorts of issues.. with
12213             // images getting pulled from server..
12214             div = document.createElement('div');
12215             div.innerHTML = this.html;
12216         }
12217         //doc.documentElement.innerHTML = htmlBody
12218          
12219         
12220         
12221         this.tpls = [];
12222         var _t = this;
12223         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12224         
12225         var tpls = this.tpls;
12226         
12227         // create a top level template from the snippet..
12228         
12229         //Roo.log(div.innerHTML);
12230         
12231         var tpl = {
12232             uid : 'master',
12233             id : this.id++,
12234             attr : false,
12235             value : false,
12236             body : div.innerHTML,
12237             
12238             forCall : false,
12239             execCall : false,
12240             dom : div,
12241             isTop : true
12242             
12243         };
12244         tpls.unshift(tpl);
12245         
12246         
12247         // compile them...
12248         this.tpls = [];
12249         Roo.each(tpls, function(tp){
12250             this.compileTpl(tp);
12251             this.tpls[tp.id] = tp;
12252         }, this);
12253         
12254         this.master = tpls[0];
12255         return this;
12256         
12257         
12258     },
12259     
12260     compileNode : function(node, istop) {
12261         // test for
12262         //Roo.log(node);
12263         
12264         
12265         // skip anything not a tag..
12266         if (node.nodeType != 1) {
12267             if (node.nodeType == 3 && !this.inPre) {
12268                 // reduce white space..
12269                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12270                 
12271             }
12272             return;
12273         }
12274         
12275         var tpl = {
12276             uid : false,
12277             id : false,
12278             attr : false,
12279             value : false,
12280             body : '',
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : false,
12285             isTop : istop
12286             
12287             
12288         };
12289         
12290         
12291         switch(true) {
12292             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12293             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12294             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12295             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12296             // no default..
12297         }
12298         
12299         
12300         if (!tpl.attr) {
12301             // just itterate children..
12302             this.iterChild(node,this.compileNode);
12303             return;
12304         }
12305         tpl.uid = this.id++;
12306         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12307         node.removeAttribute('roo-'+ tpl.attr);
12308         if (tpl.attr != 'name') {
12309             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12310             node.parentNode.replaceChild(placeholder,  node);
12311         } else {
12312             
12313             var placeholder =  document.createElement('span');
12314             placeholder.className = 'roo-tpl-' + tpl.value;
12315             node.parentNode.replaceChild(placeholder,  node);
12316         }
12317         
12318         // parent now sees '{domtplXXXX}
12319         this.iterChild(node,this.compileNode);
12320         
12321         // we should now have node body...
12322         var div = document.createElement('div');
12323         div.appendChild(node);
12324         tpl.dom = node;
12325         // this has the unfortunate side effect of converting tagged attributes
12326         // eg. href="{...}" into %7C...%7D
12327         // this has been fixed by searching for those combo's although it's a bit hacky..
12328         
12329         
12330         tpl.body = div.innerHTML;
12331         
12332         
12333          
12334         tpl.id = tpl.uid;
12335         switch(tpl.attr) {
12336             case 'for' :
12337                 switch (tpl.value) {
12338                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12339                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12340                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12341                 }
12342                 break;
12343             
12344             case 'exec':
12345                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12346                 break;
12347             
12348             case 'if':     
12349                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'name':
12353                 tpl.id  = tpl.value; // replace non characters???
12354                 break;
12355             
12356         }
12357         
12358         
12359         this.tpls.push(tpl);
12360         
12361         
12362         
12363     },
12364     
12365     
12366     
12367     
12368     /**
12369      * Compile a segment of the template into a 'sub-template'
12370      *
12371      * 
12372      * 
12373      *
12374      */
12375     compileTpl : function(tpl)
12376     {
12377         var fm = Roo.util.Format;
12378         var useF = this.disableFormats !== true;
12379         
12380         var sep = Roo.isGecko ? "+\n" : ",\n";
12381         
12382         var undef = function(str) {
12383             Roo.debug && Roo.log("Property not found :"  + str);
12384             return '';
12385         };
12386           
12387         //Roo.log(tpl.body);
12388         
12389         
12390         
12391         var fn = function(m, lbrace, name, format, args)
12392         {
12393             //Roo.log("ARGS");
12394             //Roo.log(arguments);
12395             args = args ? args.replace(/\\'/g,"'") : args;
12396             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12397             if (typeof(format) == 'undefined') {
12398                 format =  'htmlEncode'; 
12399             }
12400             if (format == 'raw' ) {
12401                 format = false;
12402             }
12403             
12404             if(name.substr(0, 6) == 'domtpl'){
12405                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12406             }
12407             
12408             // build an array of options to determine if value is undefined..
12409             
12410             // basically get 'xxxx.yyyy' then do
12411             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12412             //    (function () { Roo.log("Property not found"); return ''; })() :
12413             //    ......
12414             
12415             var udef_ar = [];
12416             var lookfor = '';
12417             Roo.each(name.split('.'), function(st) {
12418                 lookfor += (lookfor.length ? '.': '') + st;
12419                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12420             });
12421             
12422             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12423             
12424             
12425             if(format && useF){
12426                 
12427                 args = args ? ',' + args : "";
12428                  
12429                 if(format.substr(0, 5) != "this."){
12430                     format = "fm." + format + '(';
12431                 }else{
12432                     format = 'this.call("'+ format.substr(5) + '", ';
12433                     args = ", values";
12434                 }
12435                 
12436                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12437             }
12438              
12439             if (args && args.length) {
12440                 // called with xxyx.yuu:(test,test)
12441                 // change to ()
12442                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12443             }
12444             // raw.. - :raw modifier..
12445             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12446             
12447         };
12448         var body;
12449         // branched to use + in gecko and [].join() in others
12450         if(Roo.isGecko){
12451             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12452                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12453                     "';};};";
12454         }else{
12455             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12456             body.push(tpl.body.replace(/(\r\n|\n)/g,
12457                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12458             body.push("'].join('');};};");
12459             body = body.join('');
12460         }
12461         
12462         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12463        
12464         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12465         eval(body);
12466         
12467         return this;
12468     },
12469      
12470     /**
12471      * same as applyTemplate, except it's done to one of the subTemplates
12472      * when using named templates, you can do:
12473      *
12474      * var str = pl.applySubTemplate('your-name', values);
12475      *
12476      * 
12477      * @param {Number} id of the template
12478      * @param {Object} values to apply to template
12479      * @param {Object} parent (normaly the instance of this object)
12480      */
12481     applySubTemplate : function(id, values, parent)
12482     {
12483         
12484         
12485         var t = this.tpls[id];
12486         
12487         
12488         try { 
12489             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12490                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12491                 return '';
12492             }
12493         } catch(e) {
12494             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12495             Roo.log(values);
12496           
12497             return '';
12498         }
12499         try { 
12500             
12501             if(t.execCall && t.execCall.call(this, values, parent)){
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507             return '';
12508         }
12509         
12510         try {
12511             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12512             parent = t.target ? values : parent;
12513             if(t.forCall && vs instanceof Array){
12514                 var buf = [];
12515                 for(var i = 0, len = vs.length; i < len; i++){
12516                     try {
12517                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12518                     } catch (e) {
12519                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12520                         Roo.log(e.body);
12521                         //Roo.log(t.compiled);
12522                         Roo.log(vs[i]);
12523                     }   
12524                 }
12525                 return buf.join('');
12526             }
12527         } catch (e) {
12528             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12529             Roo.log(values);
12530             return '';
12531         }
12532         try {
12533             return t.compiled.call(this, vs, parent);
12534         } catch (e) {
12535             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12536             Roo.log(e.body);
12537             //Roo.log(t.compiled);
12538             Roo.log(values);
12539             return '';
12540         }
12541     },
12542
12543    
12544
12545     applyTemplate : function(values){
12546         return this.master.compiled.call(this, values, {});
12547         //var s = this.subs;
12548     },
12549
12550     apply : function(){
12551         return this.applyTemplate.apply(this, arguments);
12552     }
12553
12554  });
12555
12556 Roo.DomTemplate.from = function(el){
12557     el = Roo.getDom(el);
12558     return new Roo.Domtemplate(el.value || el.innerHTML);
12559 };/*
12560  * Based on:
12561  * Ext JS Library 1.1.1
12562  * Copyright(c) 2006-2007, Ext JS, LLC.
12563  *
12564  * Originally Released Under LGPL - original licence link has changed is not relivant.
12565  *
12566  * Fork - LGPL
12567  * <script type="text/javascript">
12568  */
12569
12570 /**
12571  * @class Roo.util.DelayedTask
12572  * Provides a convenient method of performing setTimeout where a new
12573  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12574  * You can use this class to buffer
12575  * the keypress events for a certain number of milliseconds, and perform only if they stop
12576  * for that amount of time.
12577  * @constructor The parameters to this constructor serve as defaults and are not required.
12578  * @param {Function} fn (optional) The default function to timeout
12579  * @param {Object} scope (optional) The default scope of that timeout
12580  * @param {Array} args (optional) The default Array of arguments
12581  */
12582 Roo.util.DelayedTask = function(fn, scope, args){
12583     var id = null, d, t;
12584
12585     var call = function(){
12586         var now = new Date().getTime();
12587         if(now - t >= d){
12588             clearInterval(id);
12589             id = null;
12590             fn.apply(scope, args || []);
12591         }
12592     };
12593     /**
12594      * Cancels any pending timeout and queues a new one
12595      * @param {Number} delay The milliseconds to delay
12596      * @param {Function} newFn (optional) Overrides function passed to constructor
12597      * @param {Object} newScope (optional) Overrides scope passed to constructor
12598      * @param {Array} newArgs (optional) Overrides args passed to constructor
12599      */
12600     this.delay = function(delay, newFn, newScope, newArgs){
12601         if(id && delay != d){
12602             this.cancel();
12603         }
12604         d = delay;
12605         t = new Date().getTime();
12606         fn = newFn || fn;
12607         scope = newScope || scope;
12608         args = newArgs || args;
12609         if(!id){
12610             id = setInterval(call, d);
12611         }
12612     };
12613
12614     /**
12615      * Cancel the last queued timeout
12616      */
12617     this.cancel = function(){
12618         if(id){
12619             clearInterval(id);
12620             id = null;
12621         }
12622     };
12623 };/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634  
12635 Roo.util.TaskRunner = function(interval){
12636     interval = interval || 10;
12637     var tasks = [], removeQueue = [];
12638     var id = 0;
12639     var running = false;
12640
12641     var stopThread = function(){
12642         running = false;
12643         clearInterval(id);
12644         id = 0;
12645     };
12646
12647     var startThread = function(){
12648         if(!running){
12649             running = true;
12650             id = setInterval(runTasks, interval);
12651         }
12652     };
12653
12654     var removeTask = function(task){
12655         removeQueue.push(task);
12656         if(task.onStop){
12657             task.onStop();
12658         }
12659     };
12660
12661     var runTasks = function(){
12662         if(removeQueue.length > 0){
12663             for(var i = 0, len = removeQueue.length; i < len; i++){
12664                 tasks.remove(removeQueue[i]);
12665             }
12666             removeQueue = [];
12667             if(tasks.length < 1){
12668                 stopThread();
12669                 return;
12670             }
12671         }
12672         var now = new Date().getTime();
12673         for(var i = 0, len = tasks.length; i < len; ++i){
12674             var t = tasks[i];
12675             var itime = now - t.taskRunTime;
12676             if(t.interval <= itime){
12677                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12678                 t.taskRunTime = now;
12679                 if(rt === false || t.taskRunCount === t.repeat){
12680                     removeTask(t);
12681                     return;
12682                 }
12683             }
12684             if(t.duration && t.duration <= (now - t.taskStartTime)){
12685                 removeTask(t);
12686             }
12687         }
12688     };
12689
12690     /**
12691      * Queues a new task.
12692      * @param {Object} task
12693      */
12694     this.start = function(task){
12695         tasks.push(task);
12696         task.taskStartTime = new Date().getTime();
12697         task.taskRunTime = 0;
12698         task.taskRunCount = 0;
12699         startThread();
12700         return task;
12701     };
12702
12703     this.stop = function(task){
12704         removeTask(task);
12705         return task;
12706     };
12707
12708     this.stopAll = function(){
12709         stopThread();
12710         for(var i = 0, len = tasks.length; i < len; i++){
12711             if(tasks[i].onStop){
12712                 tasks[i].onStop();
12713             }
12714         }
12715         tasks = [];
12716         removeQueue = [];
12717     };
12718 };
12719
12720 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730
12731  
12732 /**
12733  * @class Roo.util.MixedCollection
12734  * @extends Roo.util.Observable
12735  * A Collection class that maintains both numeric indexes and keys and exposes events.
12736  * @constructor
12737  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12738  * collection (defaults to false)
12739  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12740  * and return the key value for that item.  This is used when available to look up the key on items that
12741  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12742  * equivalent to providing an implementation for the {@link #getKey} method.
12743  */
12744 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12745     this.items = [];
12746     this.map = {};
12747     this.keys = [];
12748     this.length = 0;
12749     this.addEvents({
12750         /**
12751          * @event clear
12752          * Fires when the collection is cleared.
12753          */
12754         "clear" : true,
12755         /**
12756          * @event add
12757          * Fires when an item is added to the collection.
12758          * @param {Number} index The index at which the item was added.
12759          * @param {Object} o The item added.
12760          * @param {String} key The key associated with the added item.
12761          */
12762         "add" : true,
12763         /**
12764          * @event replace
12765          * Fires when an item is replaced in the collection.
12766          * @param {String} key he key associated with the new added.
12767          * @param {Object} old The item being replaced.
12768          * @param {Object} new The new item.
12769          */
12770         "replace" : true,
12771         /**
12772          * @event remove
12773          * Fires when an item is removed from the collection.
12774          * @param {Object} o The item being removed.
12775          * @param {String} key (optional) The key associated with the removed item.
12776          */
12777         "remove" : true,
12778         "sort" : true
12779     });
12780     this.allowFunctions = allowFunctions === true;
12781     if(keyFn){
12782         this.getKey = keyFn;
12783     }
12784     Roo.util.MixedCollection.superclass.constructor.call(this);
12785 };
12786
12787 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12788     allowFunctions : false,
12789     
12790 /**
12791  * Adds an item to the collection.
12792  * @param {String} key The key to associate with the item
12793  * @param {Object} o The item to add.
12794  * @return {Object} The item added.
12795  */
12796     add : function(key, o){
12797         if(arguments.length == 1){
12798             o = arguments[0];
12799             key = this.getKey(o);
12800         }
12801         if(typeof key == "undefined" || key === null){
12802             this.length++;
12803             this.items.push(o);
12804             this.keys.push(null);
12805         }else{
12806             var old = this.map[key];
12807             if(old){
12808                 return this.replace(key, o);
12809             }
12810             this.length++;
12811             this.items.push(o);
12812             this.map[key] = o;
12813             this.keys.push(key);
12814         }
12815         this.fireEvent("add", this.length-1, o, key);
12816         return o;
12817     },
12818        
12819 /**
12820   * MixedCollection has a generic way to fetch keys if you implement getKey.
12821 <pre><code>
12822 // normal way
12823 var mc = new Roo.util.MixedCollection();
12824 mc.add(someEl.dom.id, someEl);
12825 mc.add(otherEl.dom.id, otherEl);
12826 //and so on
12827
12828 // using getKey
12829 var mc = new Roo.util.MixedCollection();
12830 mc.getKey = function(el){
12831    return el.dom.id;
12832 };
12833 mc.add(someEl);
12834 mc.add(otherEl);
12835
12836 // or via the constructor
12837 var mc = new Roo.util.MixedCollection(false, function(el){
12838    return el.dom.id;
12839 });
12840 mc.add(someEl);
12841 mc.add(otherEl);
12842 </code></pre>
12843  * @param o {Object} The item for which to find the key.
12844  * @return {Object} The key for the passed item.
12845  */
12846     getKey : function(o){
12847          return o.id; 
12848     },
12849    
12850 /**
12851  * Replaces an item in the collection.
12852  * @param {String} key The key associated with the item to replace, or the item to replace.
12853  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12854  * @return {Object}  The new item.
12855  */
12856     replace : function(key, o){
12857         if(arguments.length == 1){
12858             o = arguments[0];
12859             key = this.getKey(o);
12860         }
12861         var old = this.item(key);
12862         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12863              return this.add(key, o);
12864         }
12865         var index = this.indexOfKey(key);
12866         this.items[index] = o;
12867         this.map[key] = o;
12868         this.fireEvent("replace", key, old, o);
12869         return o;
12870     },
12871    
12872 /**
12873  * Adds all elements of an Array or an Object to the collection.
12874  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12875  * an Array of values, each of which are added to the collection.
12876  */
12877     addAll : function(objs){
12878         if(arguments.length > 1 || objs instanceof Array){
12879             var args = arguments.length > 1 ? arguments : objs;
12880             for(var i = 0, len = args.length; i < len; i++){
12881                 this.add(args[i]);
12882             }
12883         }else{
12884             for(var key in objs){
12885                 if(this.allowFunctions || typeof objs[key] != "function"){
12886                     this.add(key, objs[key]);
12887                 }
12888             }
12889         }
12890     },
12891    
12892 /**
12893  * Executes the specified function once for every item in the collection, passing each
12894  * item as the first and only parameter. returning false from the function will stop the iteration.
12895  * @param {Function} fn The function to execute for each item.
12896  * @param {Object} scope (optional) The scope in which to execute the function.
12897  */
12898     each : function(fn, scope){
12899         var items = [].concat(this.items); // each safe for removal
12900         for(var i = 0, len = items.length; i < len; i++){
12901             if(fn.call(scope || items[i], items[i], i, len) === false){
12902                 break;
12903             }
12904         }
12905     },
12906    
12907 /**
12908  * Executes the specified function once for every key in the collection, passing each
12909  * key, and its associated item as the first two parameters.
12910  * @param {Function} fn The function to execute for each item.
12911  * @param {Object} scope (optional) The scope in which to execute the function.
12912  */
12913     eachKey : function(fn, scope){
12914         for(var i = 0, len = this.keys.length; i < len; i++){
12915             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12916         }
12917     },
12918    
12919 /**
12920  * Returns the first item in the collection which elicits a true return value from the
12921  * passed selection function.
12922  * @param {Function} fn The selection function to execute for each item.
12923  * @param {Object} scope (optional) The scope in which to execute the function.
12924  * @return {Object} The first item in the collection which returned true from the selection function.
12925  */
12926     find : function(fn, scope){
12927         for(var i = 0, len = this.items.length; i < len; i++){
12928             if(fn.call(scope || window, this.items[i], this.keys[i])){
12929                 return this.items[i];
12930             }
12931         }
12932         return null;
12933     },
12934    
12935 /**
12936  * Inserts an item at the specified index in the collection.
12937  * @param {Number} index The index to insert the item at.
12938  * @param {String} key The key to associate with the new item, or the item itself.
12939  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12940  * @return {Object} The item inserted.
12941  */
12942     insert : function(index, key, o){
12943         if(arguments.length == 2){
12944             o = arguments[1];
12945             key = this.getKey(o);
12946         }
12947         if(index >= this.length){
12948             return this.add(key, o);
12949         }
12950         this.length++;
12951         this.items.splice(index, 0, o);
12952         if(typeof key != "undefined" && key != null){
12953             this.map[key] = o;
12954         }
12955         this.keys.splice(index, 0, key);
12956         this.fireEvent("add", index, o, key);
12957         return o;
12958     },
12959    
12960 /**
12961  * Removed an item from the collection.
12962  * @param {Object} o The item to remove.
12963  * @return {Object} The item removed.
12964  */
12965     remove : function(o){
12966         return this.removeAt(this.indexOf(o));
12967     },
12968    
12969 /**
12970  * Remove an item from a specified index in the collection.
12971  * @param {Number} index The index within the collection of the item to remove.
12972  */
12973     removeAt : function(index){
12974         if(index < this.length && index >= 0){
12975             this.length--;
12976             var o = this.items[index];
12977             this.items.splice(index, 1);
12978             var key = this.keys[index];
12979             if(typeof key != "undefined"){
12980                 delete this.map[key];
12981             }
12982             this.keys.splice(index, 1);
12983             this.fireEvent("remove", o, key);
12984         }
12985     },
12986    
12987 /**
12988  * Removed an item associated with the passed key fom the collection.
12989  * @param {String} key The key of the item to remove.
12990  */
12991     removeKey : function(key){
12992         return this.removeAt(this.indexOfKey(key));
12993     },
12994    
12995 /**
12996  * Returns the number of items in the collection.
12997  * @return {Number} the number of items in the collection.
12998  */
12999     getCount : function(){
13000         return this.length; 
13001     },
13002    
13003 /**
13004  * Returns index within the collection of the passed Object.
13005  * @param {Object} o The item to find the index of.
13006  * @return {Number} index of the item.
13007  */
13008     indexOf : function(o){
13009         if(!this.items.indexOf){
13010             for(var i = 0, len = this.items.length; i < len; i++){
13011                 if(this.items[i] == o) return i;
13012             }
13013             return -1;
13014         }else{
13015             return this.items.indexOf(o);
13016         }
13017     },
13018    
13019 /**
13020  * Returns index within the collection of the passed key.
13021  * @param {String} key The key to find the index of.
13022  * @return {Number} index of the key.
13023  */
13024     indexOfKey : function(key){
13025         if(!this.keys.indexOf){
13026             for(var i = 0, len = this.keys.length; i < len; i++){
13027                 if(this.keys[i] == key) return i;
13028             }
13029             return -1;
13030         }else{
13031             return this.keys.indexOf(key);
13032         }
13033     },
13034    
13035 /**
13036  * Returns the item associated with the passed key OR index. Key has priority over index.
13037  * @param {String/Number} key The key or index of the item.
13038  * @return {Object} The item associated with the passed key.
13039  */
13040     item : function(key){
13041         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13042         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13043     },
13044     
13045 /**
13046  * Returns the item at the specified index.
13047  * @param {Number} index The index of the item.
13048  * @return {Object}
13049  */
13050     itemAt : function(index){
13051         return this.items[index];
13052     },
13053     
13054 /**
13055  * Returns the item associated with the passed key.
13056  * @param {String/Number} key The key of the item.
13057  * @return {Object} The item associated with the passed key.
13058  */
13059     key : function(key){
13060         return this.map[key];
13061     },
13062    
13063 /**
13064  * Returns true if the collection contains the passed Object as an item.
13065  * @param {Object} o  The Object to look for in the collection.
13066  * @return {Boolean} True if the collection contains the Object as an item.
13067  */
13068     contains : function(o){
13069         return this.indexOf(o) != -1;
13070     },
13071    
13072 /**
13073  * Returns true if the collection contains the passed Object as a key.
13074  * @param {String} key The key to look for in the collection.
13075  * @return {Boolean} True if the collection contains the Object as a key.
13076  */
13077     containsKey : function(key){
13078         return typeof this.map[key] != "undefined";
13079     },
13080    
13081 /**
13082  * Removes all items from the collection.
13083  */
13084     clear : function(){
13085         this.length = 0;
13086         this.items = [];
13087         this.keys = [];
13088         this.map = {};
13089         this.fireEvent("clear");
13090     },
13091    
13092 /**
13093  * Returns the first item in the collection.
13094  * @return {Object} the first item in the collection..
13095  */
13096     first : function(){
13097         return this.items[0]; 
13098     },
13099    
13100 /**
13101  * Returns the last item in the collection.
13102  * @return {Object} the last item in the collection..
13103  */
13104     last : function(){
13105         return this.items[this.length-1];   
13106     },
13107     
13108     _sort : function(property, dir, fn){
13109         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13110         fn = fn || function(a, b){
13111             return a-b;
13112         };
13113         var c = [], k = this.keys, items = this.items;
13114         for(var i = 0, len = items.length; i < len; i++){
13115             c[c.length] = {key: k[i], value: items[i], index: i};
13116         }
13117         c.sort(function(a, b){
13118             var v = fn(a[property], b[property]) * dsc;
13119             if(v == 0){
13120                 v = (a.index < b.index ? -1 : 1);
13121             }
13122             return v;
13123         });
13124         for(var i = 0, len = c.length; i < len; i++){
13125             items[i] = c[i].value;
13126             k[i] = c[i].key;
13127         }
13128         this.fireEvent("sort", this);
13129     },
13130     
13131     /**
13132      * Sorts this collection with the passed comparison function
13133      * @param {String} direction (optional) "ASC" or "DESC"
13134      * @param {Function} fn (optional) comparison function
13135      */
13136     sort : function(dir, fn){
13137         this._sort("value", dir, fn);
13138     },
13139     
13140     /**
13141      * Sorts this collection by keys
13142      * @param {String} direction (optional) "ASC" or "DESC"
13143      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13144      */
13145     keySort : function(dir, fn){
13146         this._sort("key", dir, fn || function(a, b){
13147             return String(a).toUpperCase()-String(b).toUpperCase();
13148         });
13149     },
13150     
13151     /**
13152      * Returns a range of items in this collection
13153      * @param {Number} startIndex (optional) defaults to 0
13154      * @param {Number} endIndex (optional) default to the last item
13155      * @return {Array} An array of items
13156      */
13157     getRange : function(start, end){
13158         var items = this.items;
13159         if(items.length < 1){
13160             return [];
13161         }
13162         start = start || 0;
13163         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13164         var r = [];
13165         if(start <= end){
13166             for(var i = start; i <= end; i++) {
13167                     r[r.length] = items[i];
13168             }
13169         }else{
13170             for(var i = start; i >= end; i--) {
13171                     r[r.length] = items[i];
13172             }
13173         }
13174         return r;
13175     },
13176         
13177     /**
13178      * Filter the <i>objects</i> in this collection by a specific property. 
13179      * Returns a new collection that has been filtered.
13180      * @param {String} property A property on your objects
13181      * @param {String/RegExp} value Either string that the property values 
13182      * should start with or a RegExp to test against the property
13183      * @return {MixedCollection} The new filtered collection
13184      */
13185     filter : function(property, value){
13186         if(!value.exec){ // not a regex
13187             value = String(value);
13188             if(value.length == 0){
13189                 return this.clone();
13190             }
13191             value = new RegExp("^" + Roo.escapeRe(value), "i");
13192         }
13193         return this.filterBy(function(o){
13194             return o && value.test(o[property]);
13195         });
13196         },
13197     
13198     /**
13199      * Filter by a function. * Returns a new collection that has been filtered.
13200      * The passed function will be called with each 
13201      * object in the collection. If the function returns true, the value is included 
13202      * otherwise it is filtered.
13203      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13204      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13205      * @return {MixedCollection} The new filtered collection
13206      */
13207     filterBy : function(fn, scope){
13208         var r = new Roo.util.MixedCollection();
13209         r.getKey = this.getKey;
13210         var k = this.keys, it = this.items;
13211         for(var i = 0, len = it.length; i < len; i++){
13212             if(fn.call(scope||this, it[i], k[i])){
13213                                 r.add(k[i], it[i]);
13214                         }
13215         }
13216         return r;
13217     },
13218     
13219     /**
13220      * Creates a duplicate of this collection
13221      * @return {MixedCollection}
13222      */
13223     clone : function(){
13224         var r = new Roo.util.MixedCollection();
13225         var k = this.keys, it = this.items;
13226         for(var i = 0, len = it.length; i < len; i++){
13227             r.add(k[i], it[i]);
13228         }
13229         r.getKey = this.getKey;
13230         return r;
13231     }
13232 });
13233 /**
13234  * Returns the item associated with the passed key or index.
13235  * @method
13236  * @param {String/Number} key The key or index of the item.
13237  * @return {Object} The item associated with the passed key.
13238  */
13239 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13240  * Based on:
13241  * Ext JS Library 1.1.1
13242  * Copyright(c) 2006-2007, Ext JS, LLC.
13243  *
13244  * Originally Released Under LGPL - original licence link has changed is not relivant.
13245  *
13246  * Fork - LGPL
13247  * <script type="text/javascript">
13248  */
13249 /**
13250  * @class Roo.util.JSON
13251  * Modified version of Douglas Crockford"s json.js that doesn"t
13252  * mess with the Object prototype 
13253  * http://www.json.org/js.html
13254  * @singleton
13255  */
13256 Roo.util.JSON = new (function(){
13257     var useHasOwn = {}.hasOwnProperty ? true : false;
13258     
13259     // crashes Safari in some instances
13260     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13261     
13262     var pad = function(n) {
13263         return n < 10 ? "0" + n : n;
13264     };
13265     
13266     var m = {
13267         "\b": '\\b',
13268         "\t": '\\t',
13269         "\n": '\\n',
13270         "\f": '\\f',
13271         "\r": '\\r',
13272         '"' : '\\"',
13273         "\\": '\\\\'
13274     };
13275
13276     var encodeString = function(s){
13277         if (/["\\\x00-\x1f]/.test(s)) {
13278             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13279                 var c = m[b];
13280                 if(c){
13281                     return c;
13282                 }
13283                 c = b.charCodeAt();
13284                 return "\\u00" +
13285                     Math.floor(c / 16).toString(16) +
13286                     (c % 16).toString(16);
13287             }) + '"';
13288         }
13289         return '"' + s + '"';
13290     };
13291     
13292     var encodeArray = function(o){
13293         var a = ["["], b, i, l = o.length, v;
13294             for (i = 0; i < l; i += 1) {
13295                 v = o[i];
13296                 switch (typeof v) {
13297                     case "undefined":
13298                     case "function":
13299                     case "unknown":
13300                         break;
13301                     default:
13302                         if (b) {
13303                             a.push(',');
13304                         }
13305                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13306                         b = true;
13307                 }
13308             }
13309             a.push("]");
13310             return a.join("");
13311     };
13312     
13313     var encodeDate = function(o){
13314         return '"' + o.getFullYear() + "-" +
13315                 pad(o.getMonth() + 1) + "-" +
13316                 pad(o.getDate()) + "T" +
13317                 pad(o.getHours()) + ":" +
13318                 pad(o.getMinutes()) + ":" +
13319                 pad(o.getSeconds()) + '"';
13320     };
13321     
13322     /**
13323      * Encodes an Object, Array or other value
13324      * @param {Mixed} o The variable to encode
13325      * @return {String} The JSON string
13326      */
13327     this.encode = function(o)
13328     {
13329         // should this be extended to fully wrap stringify..
13330         
13331         if(typeof o == "undefined" || o === null){
13332             return "null";
13333         }else if(o instanceof Array){
13334             return encodeArray(o);
13335         }else if(o instanceof Date){
13336             return encodeDate(o);
13337         }else if(typeof o == "string"){
13338             return encodeString(o);
13339         }else if(typeof o == "number"){
13340             return isFinite(o) ? String(o) : "null";
13341         }else if(typeof o == "boolean"){
13342             return String(o);
13343         }else {
13344             var a = ["{"], b, i, v;
13345             for (i in o) {
13346                 if(!useHasOwn || o.hasOwnProperty(i)) {
13347                     v = o[i];
13348                     switch (typeof v) {
13349                     case "undefined":
13350                     case "function":
13351                     case "unknown":
13352                         break;
13353                     default:
13354                         if(b){
13355                             a.push(',');
13356                         }
13357                         a.push(this.encode(i), ":",
13358                                 v === null ? "null" : this.encode(v));
13359                         b = true;
13360                     }
13361                 }
13362             }
13363             a.push("}");
13364             return a.join("");
13365         }
13366     };
13367     
13368     /**
13369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13370      * @param {String} json The JSON string
13371      * @return {Object} The resulting object
13372      */
13373     this.decode = function(json){
13374         
13375         return  /** eval:var:json */ eval("(" + json + ')');
13376     };
13377 })();
13378 /** 
13379  * Shorthand for {@link Roo.util.JSON#encode}
13380  * @member Roo encode 
13381  * @method */
13382 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13383 /** 
13384  * Shorthand for {@link Roo.util.JSON#decode}
13385  * @member Roo decode 
13386  * @method */
13387 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13388 /*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398  
13399 /**
13400  * @class Roo.util.Format
13401  * Reusable data formatting functions
13402  * @singleton
13403  */
13404 Roo.util.Format = function(){
13405     var trimRe = /^\s+|\s+$/g;
13406     return {
13407         /**
13408          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13409          * @param {String} value The string to truncate
13410          * @param {Number} length The maximum length to allow before truncating
13411          * @return {String} The converted text
13412          */
13413         ellipsis : function(value, len){
13414             if(value && value.length > len){
13415                 return value.substr(0, len-3)+"...";
13416             }
13417             return value;
13418         },
13419
13420         /**
13421          * Checks a reference and converts it to empty string if it is undefined
13422          * @param {Mixed} value Reference to check
13423          * @return {Mixed} Empty string if converted, otherwise the original value
13424          */
13425         undef : function(value){
13426             return typeof value != "undefined" ? value : "";
13427         },
13428
13429         /**
13430          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13431          * @param {String} value The string to encode
13432          * @return {String} The encoded text
13433          */
13434         htmlEncode : function(value){
13435             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13436         },
13437
13438         /**
13439          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13440          * @param {String} value The string to decode
13441          * @return {String} The decoded text
13442          */
13443         htmlDecode : function(value){
13444             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13445         },
13446
13447         /**
13448          * Trims any whitespace from either side of a string
13449          * @param {String} value The text to trim
13450          * @return {String} The trimmed text
13451          */
13452         trim : function(value){
13453             return String(value).replace(trimRe, "");
13454         },
13455
13456         /**
13457          * Returns a substring from within an original string
13458          * @param {String} value The original text
13459          * @param {Number} start The start index of the substring
13460          * @param {Number} length The length of the substring
13461          * @return {String} The substring
13462          */
13463         substr : function(value, start, length){
13464             return String(value).substr(start, length);
13465         },
13466
13467         /**
13468          * Converts a string to all lower case letters
13469          * @param {String} value The text to convert
13470          * @return {String} The converted text
13471          */
13472         lowercase : function(value){
13473             return String(value).toLowerCase();
13474         },
13475
13476         /**
13477          * Converts a string to all upper case letters
13478          * @param {String} value The text to convert
13479          * @return {String} The converted text
13480          */
13481         uppercase : function(value){
13482             return String(value).toUpperCase();
13483         },
13484
13485         /**
13486          * Converts the first character only of a string to upper case
13487          * @param {String} value The text to convert
13488          * @return {String} The converted text
13489          */
13490         capitalize : function(value){
13491             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13492         },
13493
13494         // private
13495         call : function(value, fn){
13496             if(arguments.length > 2){
13497                 var args = Array.prototype.slice.call(arguments, 2);
13498                 args.unshift(value);
13499                  
13500                 return /** eval:var:value */  eval(fn).apply(window, args);
13501             }else{
13502                 /** eval:var:value */
13503                 return /** eval:var:value */ eval(fn).call(window, value);
13504             }
13505         },
13506
13507        
13508         /**
13509          * safer version of Math.toFixed..??/
13510          * @param {Number/String} value The numeric value to format
13511          * @param {Number/String} value Decimal places 
13512          * @return {String} The formatted currency string
13513          */
13514         toFixed : function(v, n)
13515         {
13516             // why not use to fixed - precision is buggered???
13517             if (!n) {
13518                 return Math.round(v-0);
13519             }
13520             var fact = Math.pow(10,n+1);
13521             v = (Math.round((v-0)*fact))/fact;
13522             var z = (''+fact).substring(2);
13523             if (v == Math.floor(v)) {
13524                 return Math.floor(v) + '.' + z;
13525             }
13526             
13527             // now just padd decimals..
13528             var ps = String(v).split('.');
13529             var fd = (ps[1] + z);
13530             var r = fd.substring(0,n); 
13531             var rm = fd.substring(n); 
13532             if (rm < 5) {
13533                 return ps[0] + '.' + r;
13534             }
13535             r*=1; // turn it into a number;
13536             r++;
13537             if (String(r).length != n) {
13538                 ps[0]*=1;
13539                 ps[0]++;
13540                 r = String(r).substring(1); // chop the end off.
13541             }
13542             
13543             return ps[0] + '.' + r;
13544              
13545         },
13546         
13547         /**
13548          * Format a number as US currency
13549          * @param {Number/String} value The numeric value to format
13550          * @return {String} The formatted currency string
13551          */
13552         usMoney : function(v){
13553             return '$' + Roo.util.Format.number(v);
13554         },
13555         
13556         /**
13557          * Format a number
13558          * eventually this should probably emulate php's number_format
13559          * @param {Number/String} value The numeric value to format
13560          * @param {Number} decimals number of decimal places
13561          * @return {String} The formatted currency string
13562          */
13563         number : function(v,decimals)
13564         {
13565             // multiply and round.
13566             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13567             var mul = Math.pow(10, decimals);
13568             var zero = String(mul).substring(1);
13569             v = (Math.round((v-0)*mul))/mul;
13570             
13571             // if it's '0' number.. then
13572             
13573             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13574             v = String(v);
13575             var ps = v.split('.');
13576             var whole = ps[0];
13577             
13578             
13579             var r = /(\d+)(\d{3})/;
13580             // add comma's
13581             while (r.test(whole)) {
13582                 whole = whole.replace(r, '$1' + ',' + '$2');
13583             }
13584             
13585             
13586             var sub = ps[1] ?
13587                     // has decimals..
13588                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13589                     // does not have decimals
13590                     (decimals ? ('.' + zero) : '');
13591             
13592             
13593             return whole + sub ;
13594         },
13595         
13596         /**
13597          * Parse a value into a formatted date using the specified format pattern.
13598          * @param {Mixed} value The value to format
13599          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13600          * @return {String} The formatted date string
13601          */
13602         date : function(v, format){
13603             if(!v){
13604                 return "";
13605             }
13606             if(!(v instanceof Date)){
13607                 v = new Date(Date.parse(v));
13608             }
13609             return v.dateFormat(format || "m/d/Y");
13610         },
13611
13612         /**
13613          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13614          * @param {String} format Any valid date format string
13615          * @return {Function} The date formatting function
13616          */
13617         dateRenderer : function(format){
13618             return function(v){
13619                 return Roo.util.Format.date(v, format);  
13620             };
13621         },
13622
13623         // private
13624         stripTagsRE : /<\/?[^>]+>/gi,
13625         
13626         /**
13627          * Strips all HTML tags
13628          * @param {Mixed} value The text from which to strip tags
13629          * @return {String} The stripped text
13630          */
13631         stripTags : function(v){
13632             return !v ? v : String(v).replace(this.stripTagsRE, "");
13633         }
13634     };
13635 }();/*
13636  * Based on:
13637  * Ext JS Library 1.1.1
13638  * Copyright(c) 2006-2007, Ext JS, LLC.
13639  *
13640  * Originally Released Under LGPL - original licence link has changed is not relivant.
13641  *
13642  * Fork - LGPL
13643  * <script type="text/javascript">
13644  */
13645
13646
13647  
13648
13649 /**
13650  * @class Roo.MasterTemplate
13651  * @extends Roo.Template
13652  * Provides a template that can have child templates. The syntax is:
13653 <pre><code>
13654 var t = new Roo.MasterTemplate(
13655         '&lt;select name="{name}"&gt;',
13656                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13657         '&lt;/select&gt;'
13658 );
13659 t.add('options', {value: 'foo', text: 'bar'});
13660 // or you can add multiple child elements in one shot
13661 t.addAll('options', [
13662     {value: 'foo', text: 'bar'},
13663     {value: 'foo2', text: 'bar2'},
13664     {value: 'foo3', text: 'bar3'}
13665 ]);
13666 // then append, applying the master template values
13667 t.append('my-form', {name: 'my-select'});
13668 </code></pre>
13669 * A name attribute for the child template is not required if you have only one child
13670 * template or you want to refer to them by index.
13671  */
13672 Roo.MasterTemplate = function(){
13673     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13674     this.originalHtml = this.html;
13675     var st = {};
13676     var m, re = this.subTemplateRe;
13677     re.lastIndex = 0;
13678     var subIndex = 0;
13679     while(m = re.exec(this.html)){
13680         var name = m[1], content = m[2];
13681         st[subIndex] = {
13682             name: name,
13683             index: subIndex,
13684             buffer: [],
13685             tpl : new Roo.Template(content)
13686         };
13687         if(name){
13688             st[name] = st[subIndex];
13689         }
13690         st[subIndex].tpl.compile();
13691         st[subIndex].tpl.call = this.call.createDelegate(this);
13692         subIndex++;
13693     }
13694     this.subCount = subIndex;
13695     this.subs = st;
13696 };
13697 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13698     /**
13699     * The regular expression used to match sub templates
13700     * @type RegExp
13701     * @property
13702     */
13703     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13704
13705     /**
13706      * Applies the passed values to a child template.
13707      * @param {String/Number} name (optional) The name or index of the child template
13708      * @param {Array/Object} values The values to be applied to the template
13709      * @return {MasterTemplate} this
13710      */
13711      add : function(name, values){
13712         if(arguments.length == 1){
13713             values = arguments[0];
13714             name = 0;
13715         }
13716         var s = this.subs[name];
13717         s.buffer[s.buffer.length] = s.tpl.apply(values);
13718         return this;
13719     },
13720
13721     /**
13722      * Applies all the passed values to a child template.
13723      * @param {String/Number} name (optional) The name or index of the child template
13724      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13725      * @param {Boolean} reset (optional) True to reset the template first
13726      * @return {MasterTemplate} this
13727      */
13728     fill : function(name, values, reset){
13729         var a = arguments;
13730         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13731             values = a[0];
13732             name = 0;
13733             reset = a[1];
13734         }
13735         if(reset){
13736             this.reset();
13737         }
13738         for(var i = 0, len = values.length; i < len; i++){
13739             this.add(name, values[i]);
13740         }
13741         return this;
13742     },
13743
13744     /**
13745      * Resets the template for reuse
13746      * @return {MasterTemplate} this
13747      */
13748      reset : function(){
13749         var s = this.subs;
13750         for(var i = 0; i < this.subCount; i++){
13751             s[i].buffer = [];
13752         }
13753         return this;
13754     },
13755
13756     applyTemplate : function(values){
13757         var s = this.subs;
13758         var replaceIndex = -1;
13759         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13760             return s[++replaceIndex].buffer.join("");
13761         });
13762         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13763     },
13764
13765     apply : function(){
13766         return this.applyTemplate.apply(this, arguments);
13767     },
13768
13769     compile : function(){return this;}
13770 });
13771
13772 /**
13773  * Alias for fill().
13774  * @method
13775  */
13776 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13777  /**
13778  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13779  * var tpl = Roo.MasterTemplate.from('element-id');
13780  * @param {String/HTMLElement} el
13781  * @param {Object} config
13782  * @static
13783  */
13784 Roo.MasterTemplate.from = function(el, config){
13785     el = Roo.getDom(el);
13786     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13787 };/*
13788  * Based on:
13789  * Ext JS Library 1.1.1
13790  * Copyright(c) 2006-2007, Ext JS, LLC.
13791  *
13792  * Originally Released Under LGPL - original licence link has changed is not relivant.
13793  *
13794  * Fork - LGPL
13795  * <script type="text/javascript">
13796  */
13797
13798  
13799 /**
13800  * @class Roo.util.CSS
13801  * Utility class for manipulating CSS rules
13802  * @singleton
13803  */
13804 Roo.util.CSS = function(){
13805         var rules = null;
13806         var doc = document;
13807
13808     var camelRe = /(-[a-z])/gi;
13809     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13810
13811    return {
13812    /**
13813     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13814     * tag and appended to the HEAD of the document.
13815     * @param {String|Object} cssText The text containing the css rules
13816     * @param {String} id An id to add to the stylesheet for later removal
13817     * @return {StyleSheet}
13818     */
13819     createStyleSheet : function(cssText, id){
13820         var ss;
13821         var head = doc.getElementsByTagName("head")[0];
13822         var nrules = doc.createElement("style");
13823         nrules.setAttribute("type", "text/css");
13824         if(id){
13825             nrules.setAttribute("id", id);
13826         }
13827         if (typeof(cssText) != 'string') {
13828             // support object maps..
13829             // not sure if this a good idea.. 
13830             // perhaps it should be merged with the general css handling
13831             // and handle js style props.
13832             var cssTextNew = [];
13833             for(var n in cssText) {
13834                 var citems = [];
13835                 for(var k in cssText[n]) {
13836                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13837                 }
13838                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13839                 
13840             }
13841             cssText = cssTextNew.join("\n");
13842             
13843         }
13844        
13845        
13846        if(Roo.isIE){
13847            head.appendChild(nrules);
13848            ss = nrules.styleSheet;
13849            ss.cssText = cssText;
13850        }else{
13851            try{
13852                 nrules.appendChild(doc.createTextNode(cssText));
13853            }catch(e){
13854                nrules.cssText = cssText; 
13855            }
13856            head.appendChild(nrules);
13857            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13858        }
13859        this.cacheStyleSheet(ss);
13860        return ss;
13861    },
13862
13863    /**
13864     * Removes a style or link tag by id
13865     * @param {String} id The id of the tag
13866     */
13867    removeStyleSheet : function(id){
13868        var existing = doc.getElementById(id);
13869        if(existing){
13870            existing.parentNode.removeChild(existing);
13871        }
13872    },
13873
13874    /**
13875     * Dynamically swaps an existing stylesheet reference for a new one
13876     * @param {String} id The id of an existing link tag to remove
13877     * @param {String} url The href of the new stylesheet to include
13878     */
13879    swapStyleSheet : function(id, url){
13880        this.removeStyleSheet(id);
13881        var ss = doc.createElement("link");
13882        ss.setAttribute("rel", "stylesheet");
13883        ss.setAttribute("type", "text/css");
13884        ss.setAttribute("id", id);
13885        ss.setAttribute("href", url);
13886        doc.getElementsByTagName("head")[0].appendChild(ss);
13887    },
13888    
13889    /**
13890     * Refresh the rule cache if you have dynamically added stylesheets
13891     * @return {Object} An object (hash) of rules indexed by selector
13892     */
13893    refreshCache : function(){
13894        return this.getRules(true);
13895    },
13896
13897    // private
13898    cacheStyleSheet : function(stylesheet){
13899        if(!rules){
13900            rules = {};
13901        }
13902        try{// try catch for cross domain access issue
13903            var ssRules = stylesheet.cssRules || stylesheet.rules;
13904            for(var j = ssRules.length-1; j >= 0; --j){
13905                rules[ssRules[j].selectorText] = ssRules[j];
13906            }
13907        }catch(e){}
13908    },
13909    
13910    /**
13911     * Gets all css rules for the document
13912     * @param {Boolean} refreshCache true to refresh the internal cache
13913     * @return {Object} An object (hash) of rules indexed by selector
13914     */
13915    getRules : function(refreshCache){
13916                 if(rules == null || refreshCache){
13917                         rules = {};
13918                         var ds = doc.styleSheets;
13919                         for(var i =0, len = ds.length; i < len; i++){
13920                             try{
13921                         this.cacheStyleSheet(ds[i]);
13922                     }catch(e){} 
13923                 }
13924                 }
13925                 return rules;
13926         },
13927         
13928         /**
13929     * Gets an an individual CSS rule by selector(s)
13930     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13931     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13932     * @return {CSSRule} The CSS rule or null if one is not found
13933     */
13934    getRule : function(selector, refreshCache){
13935                 var rs = this.getRules(refreshCache);
13936                 if(!(selector instanceof Array)){
13937                     return rs[selector];
13938                 }
13939                 for(var i = 0; i < selector.length; i++){
13940                         if(rs[selector[i]]){
13941                                 return rs[selector[i]];
13942                         }
13943                 }
13944                 return null;
13945         },
13946         
13947         
13948         /**
13949     * Updates a rule property
13950     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13951     * @param {String} property The css property
13952     * @param {String} value The new value for the property
13953     * @return {Boolean} true If a rule was found and updated
13954     */
13955    updateRule : function(selector, property, value){
13956                 if(!(selector instanceof Array)){
13957                         var rule = this.getRule(selector);
13958                         if(rule){
13959                                 rule.style[property.replace(camelRe, camelFn)] = value;
13960                                 return true;
13961                         }
13962                 }else{
13963                         for(var i = 0; i < selector.length; i++){
13964                                 if(this.updateRule(selector[i], property, value)){
13965                                         return true;
13966                                 }
13967                         }
13968                 }
13969                 return false;
13970         }
13971    };   
13972 }();/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982
13983  
13984
13985 /**
13986  * @class Roo.util.ClickRepeater
13987  * @extends Roo.util.Observable
13988  * 
13989  * A wrapper class which can be applied to any element. Fires a "click" event while the
13990  * mouse is pressed. The interval between firings may be specified in the config but
13991  * defaults to 10 milliseconds.
13992  * 
13993  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13994  * 
13995  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13996  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13997  * Similar to an autorepeat key delay.
13998  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13999  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14000  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14001  *           "interval" and "delay" are ignored. "immediate" is honored.
14002  * @cfg {Boolean} preventDefault True to prevent the default click event
14003  * @cfg {Boolean} stopDefault True to stop the default click event
14004  * 
14005  * @history
14006  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14007  *     2007-02-02 jvs Renamed to ClickRepeater
14008  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14009  *
14010  *  @constructor
14011  * @param {String/HTMLElement/Element} el The element to listen on
14012  * @param {Object} config
14013  **/
14014 Roo.util.ClickRepeater = function(el, config)
14015 {
14016     this.el = Roo.get(el);
14017     this.el.unselectable();
14018
14019     Roo.apply(this, config);
14020
14021     this.addEvents({
14022     /**
14023      * @event mousedown
14024      * Fires when the mouse button is depressed.
14025      * @param {Roo.util.ClickRepeater} this
14026      */
14027         "mousedown" : true,
14028     /**
14029      * @event click
14030      * Fires on a specified interval during the time the element is pressed.
14031      * @param {Roo.util.ClickRepeater} this
14032      */
14033         "click" : true,
14034     /**
14035      * @event mouseup
14036      * Fires when the mouse key is released.
14037      * @param {Roo.util.ClickRepeater} this
14038      */
14039         "mouseup" : true
14040     });
14041
14042     this.el.on("mousedown", this.handleMouseDown, this);
14043     if(this.preventDefault || this.stopDefault){
14044         this.el.on("click", function(e){
14045             if(this.preventDefault){
14046                 e.preventDefault();
14047             }
14048             if(this.stopDefault){
14049                 e.stopEvent();
14050             }
14051         }, this);
14052     }
14053
14054     // allow inline handler
14055     if(this.handler){
14056         this.on("click", this.handler,  this.scope || this);
14057     }
14058
14059     Roo.util.ClickRepeater.superclass.constructor.call(this);
14060 };
14061
14062 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14063     interval : 20,
14064     delay: 250,
14065     preventDefault : true,
14066     stopDefault : false,
14067     timer : 0,
14068
14069     // private
14070     handleMouseDown : function(){
14071         clearTimeout(this.timer);
14072         this.el.blur();
14073         if(this.pressClass){
14074             this.el.addClass(this.pressClass);
14075         }
14076         this.mousedownTime = new Date();
14077
14078         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14079         this.el.on("mouseout", this.handleMouseOut, this);
14080
14081         this.fireEvent("mousedown", this);
14082         this.fireEvent("click", this);
14083         
14084         this.timer = this.click.defer(this.delay || this.interval, this);
14085     },
14086
14087     // private
14088     click : function(){
14089         this.fireEvent("click", this);
14090         this.timer = this.click.defer(this.getInterval(), this);
14091     },
14092
14093     // private
14094     getInterval: function(){
14095         if(!this.accelerate){
14096             return this.interval;
14097         }
14098         var pressTime = this.mousedownTime.getElapsed();
14099         if(pressTime < 500){
14100             return 400;
14101         }else if(pressTime < 1700){
14102             return 320;
14103         }else if(pressTime < 2600){
14104             return 250;
14105         }else if(pressTime < 3500){
14106             return 180;
14107         }else if(pressTime < 4400){
14108             return 140;
14109         }else if(pressTime < 5300){
14110             return 80;
14111         }else if(pressTime < 6200){
14112             return 50;
14113         }else{
14114             return 10;
14115         }
14116     },
14117
14118     // private
14119     handleMouseOut : function(){
14120         clearTimeout(this.timer);
14121         if(this.pressClass){
14122             this.el.removeClass(this.pressClass);
14123         }
14124         this.el.on("mouseover", this.handleMouseReturn, this);
14125     },
14126
14127     // private
14128     handleMouseReturn : function(){
14129         this.el.un("mouseover", this.handleMouseReturn);
14130         if(this.pressClass){
14131             this.el.addClass(this.pressClass);
14132         }
14133         this.click();
14134     },
14135
14136     // private
14137     handleMouseUp : function(){
14138         clearTimeout(this.timer);
14139         this.el.un("mouseover", this.handleMouseReturn);
14140         this.el.un("mouseout", this.handleMouseOut);
14141         Roo.get(document).un("mouseup", this.handleMouseUp);
14142         this.el.removeClass(this.pressClass);
14143         this.fireEvent("mouseup", this);
14144     }
14145 });/*
14146  * Based on:
14147  * Ext JS Library 1.1.1
14148  * Copyright(c) 2006-2007, Ext JS, LLC.
14149  *
14150  * Originally Released Under LGPL - original licence link has changed is not relivant.
14151  *
14152  * Fork - LGPL
14153  * <script type="text/javascript">
14154  */
14155
14156  
14157 /**
14158  * @class Roo.KeyNav
14159  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14160  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14161  * way to implement custom navigation schemes for any UI component.</p>
14162  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14163  * pageUp, pageDown, del, home, end.  Usage:</p>
14164  <pre><code>
14165 var nav = new Roo.KeyNav("my-element", {
14166     "left" : function(e){
14167         this.moveLeft(e.ctrlKey);
14168     },
14169     "right" : function(e){
14170         this.moveRight(e.ctrlKey);
14171     },
14172     "enter" : function(e){
14173         this.save();
14174     },
14175     scope : this
14176 });
14177 </code></pre>
14178  * @constructor
14179  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14180  * @param {Object} config The config
14181  */
14182 Roo.KeyNav = function(el, config){
14183     this.el = Roo.get(el);
14184     Roo.apply(this, config);
14185     if(!this.disabled){
14186         this.disabled = true;
14187         this.enable();
14188     }
14189 };
14190
14191 Roo.KeyNav.prototype = {
14192     /**
14193      * @cfg {Boolean} disabled
14194      * True to disable this KeyNav instance (defaults to false)
14195      */
14196     disabled : false,
14197     /**
14198      * @cfg {String} defaultEventAction
14199      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14200      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14201      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14202      */
14203     defaultEventAction: "stopEvent",
14204     /**
14205      * @cfg {Boolean} forceKeyDown
14206      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14207      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14208      * handle keydown instead of keypress.
14209      */
14210     forceKeyDown : false,
14211
14212     // private
14213     prepareEvent : function(e){
14214         var k = e.getKey();
14215         var h = this.keyToHandler[k];
14216         //if(h && this[h]){
14217         //    e.stopPropagation();
14218         //}
14219         if(Roo.isSafari && h && k >= 37 && k <= 40){
14220             e.stopEvent();
14221         }
14222     },
14223
14224     // private
14225     relay : function(e){
14226         var k = e.getKey();
14227         var h = this.keyToHandler[k];
14228         if(h && this[h]){
14229             if(this.doRelay(e, this[h], h) !== true){
14230                 e[this.defaultEventAction]();
14231             }
14232         }
14233     },
14234
14235     // private
14236     doRelay : function(e, h, hname){
14237         return h.call(this.scope || this, e);
14238     },
14239
14240     // possible handlers
14241     enter : false,
14242     left : false,
14243     right : false,
14244     up : false,
14245     down : false,
14246     tab : false,
14247     esc : false,
14248     pageUp : false,
14249     pageDown : false,
14250     del : false,
14251     home : false,
14252     end : false,
14253
14254     // quick lookup hash
14255     keyToHandler : {
14256         37 : "left",
14257         39 : "right",
14258         38 : "up",
14259         40 : "down",
14260         33 : "pageUp",
14261         34 : "pageDown",
14262         46 : "del",
14263         36 : "home",
14264         35 : "end",
14265         13 : "enter",
14266         27 : "esc",
14267         9  : "tab"
14268     },
14269
14270         /**
14271          * Enable this KeyNav
14272          */
14273         enable: function(){
14274                 if(this.disabled){
14275             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14276             // the EventObject will normalize Safari automatically
14277             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14278                 this.el.on("keydown", this.relay,  this);
14279             }else{
14280                 this.el.on("keydown", this.prepareEvent,  this);
14281                 this.el.on("keypress", this.relay,  this);
14282             }
14283                     this.disabled = false;
14284                 }
14285         },
14286
14287         /**
14288          * Disable this KeyNav
14289          */
14290         disable: function(){
14291                 if(!this.disabled){
14292                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14293                 this.el.un("keydown", this.relay);
14294             }else{
14295                 this.el.un("keydown", this.prepareEvent);
14296                 this.el.un("keypress", this.relay);
14297             }
14298                     this.disabled = true;
14299                 }
14300         }
14301 };/*
14302  * Based on:
14303  * Ext JS Library 1.1.1
14304  * Copyright(c) 2006-2007, Ext JS, LLC.
14305  *
14306  * Originally Released Under LGPL - original licence link has changed is not relivant.
14307  *
14308  * Fork - LGPL
14309  * <script type="text/javascript">
14310  */
14311
14312  
14313 /**
14314  * @class Roo.KeyMap
14315  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14316  * The constructor accepts the same config object as defined by {@link #addBinding}.
14317  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14318  * combination it will call the function with this signature (if the match is a multi-key
14319  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14320  * A KeyMap can also handle a string representation of keys.<br />
14321  * Usage:
14322  <pre><code>
14323 // map one key by key code
14324 var map = new Roo.KeyMap("my-element", {
14325     key: 13, // or Roo.EventObject.ENTER
14326     fn: myHandler,
14327     scope: myObject
14328 });
14329
14330 // map multiple keys to one action by string
14331 var map = new Roo.KeyMap("my-element", {
14332     key: "a\r\n\t",
14333     fn: myHandler,
14334     scope: myObject
14335 });
14336
14337 // map multiple keys to multiple actions by strings and array of codes
14338 var map = new Roo.KeyMap("my-element", [
14339     {
14340         key: [10,13],
14341         fn: function(){ alert("Return was pressed"); }
14342     }, {
14343         key: "abc",
14344         fn: function(){ alert('a, b or c was pressed'); }
14345     }, {
14346         key: "\t",
14347         ctrl:true,
14348         shift:true,
14349         fn: function(){ alert('Control + shift + tab was pressed.'); }
14350     }
14351 ]);
14352 </code></pre>
14353  * <b>Note: A KeyMap starts enabled</b>
14354  * @constructor
14355  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14356  * @param {Object} config The config (see {@link #addBinding})
14357  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14358  */
14359 Roo.KeyMap = function(el, config, eventName){
14360     this.el  = Roo.get(el);
14361     this.eventName = eventName || "keydown";
14362     this.bindings = [];
14363     if(config){
14364         this.addBinding(config);
14365     }
14366     this.enable();
14367 };
14368
14369 Roo.KeyMap.prototype = {
14370     /**
14371      * True to stop the event from bubbling and prevent the default browser action if the
14372      * key was handled by the KeyMap (defaults to false)
14373      * @type Boolean
14374      */
14375     stopEvent : false,
14376
14377     /**
14378      * Add a new binding to this KeyMap. The following config object properties are supported:
14379      * <pre>
14380 Property    Type             Description
14381 ----------  ---------------  ----------------------------------------------------------------------
14382 key         String/Array     A single keycode or an array of keycodes to handle
14383 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14384 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14385 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14386 fn          Function         The function to call when KeyMap finds the expected key combination
14387 scope       Object           The scope of the callback function
14388 </pre>
14389      *
14390      * Usage:
14391      * <pre><code>
14392 // Create a KeyMap
14393 var map = new Roo.KeyMap(document, {
14394     key: Roo.EventObject.ENTER,
14395     fn: handleKey,
14396     scope: this
14397 });
14398
14399 //Add a new binding to the existing KeyMap later
14400 map.addBinding({
14401     key: 'abc',
14402     shift: true,
14403     fn: handleKey,
14404     scope: this
14405 });
14406 </code></pre>
14407      * @param {Object/Array} config A single KeyMap config or an array of configs
14408      */
14409         addBinding : function(config){
14410         if(config instanceof Array){
14411             for(var i = 0, len = config.length; i < len; i++){
14412                 this.addBinding(config[i]);
14413             }
14414             return;
14415         }
14416         var keyCode = config.key,
14417             shift = config.shift, 
14418             ctrl = config.ctrl, 
14419             alt = config.alt,
14420             fn = config.fn,
14421             scope = config.scope;
14422         if(typeof keyCode == "string"){
14423             var ks = [];
14424             var keyString = keyCode.toUpperCase();
14425             for(var j = 0, len = keyString.length; j < len; j++){
14426                 ks.push(keyString.charCodeAt(j));
14427             }
14428             keyCode = ks;
14429         }
14430         var keyArray = keyCode instanceof Array;
14431         var handler = function(e){
14432             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14433                 var k = e.getKey();
14434                 if(keyArray){
14435                     for(var i = 0, len = keyCode.length; i < len; i++){
14436                         if(keyCode[i] == k){
14437                           if(this.stopEvent){
14438                               e.stopEvent();
14439                           }
14440                           fn.call(scope || window, k, e);
14441                           return;
14442                         }
14443                     }
14444                 }else{
14445                     if(k == keyCode){
14446                         if(this.stopEvent){
14447                            e.stopEvent();
14448                         }
14449                         fn.call(scope || window, k, e);
14450                     }
14451                 }
14452             }
14453         };
14454         this.bindings.push(handler);  
14455         },
14456
14457     /**
14458      * Shorthand for adding a single key listener
14459      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14460      * following options:
14461      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14462      * @param {Function} fn The function to call
14463      * @param {Object} scope (optional) The scope of the function
14464      */
14465     on : function(key, fn, scope){
14466         var keyCode, shift, ctrl, alt;
14467         if(typeof key == "object" && !(key instanceof Array)){
14468             keyCode = key.key;
14469             shift = key.shift;
14470             ctrl = key.ctrl;
14471             alt = key.alt;
14472         }else{
14473             keyCode = key;
14474         }
14475         this.addBinding({
14476             key: keyCode,
14477             shift: shift,
14478             ctrl: ctrl,
14479             alt: alt,
14480             fn: fn,
14481             scope: scope
14482         })
14483     },
14484
14485     // private
14486     handleKeyDown : function(e){
14487             if(this.enabled){ //just in case
14488             var b = this.bindings;
14489             for(var i = 0, len = b.length; i < len; i++){
14490                 b[i].call(this, e);
14491             }
14492             }
14493         },
14494         
14495         /**
14496          * Returns true if this KeyMap is enabled
14497          * @return {Boolean} 
14498          */
14499         isEnabled : function(){
14500             return this.enabled;  
14501         },
14502         
14503         /**
14504          * Enables this KeyMap
14505          */
14506         enable: function(){
14507                 if(!this.enabled){
14508                     this.el.on(this.eventName, this.handleKeyDown, this);
14509                     this.enabled = true;
14510                 }
14511         },
14512
14513         /**
14514          * Disable this KeyMap
14515          */
14516         disable: function(){
14517                 if(this.enabled){
14518                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14519                     this.enabled = false;
14520                 }
14521         }
14522 };/*
14523  * Based on:
14524  * Ext JS Library 1.1.1
14525  * Copyright(c) 2006-2007, Ext JS, LLC.
14526  *
14527  * Originally Released Under LGPL - original licence link has changed is not relivant.
14528  *
14529  * Fork - LGPL
14530  * <script type="text/javascript">
14531  */
14532
14533  
14534 /**
14535  * @class Roo.util.TextMetrics
14536  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14537  * wide, in pixels, a given block of text will be.
14538  * @singleton
14539  */
14540 Roo.util.TextMetrics = function(){
14541     var shared;
14542     return {
14543         /**
14544          * Measures the size of the specified text
14545          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14546          * that can affect the size of the rendered text
14547          * @param {String} text The text to measure
14548          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14549          * in order to accurately measure the text height
14550          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14551          */
14552         measure : function(el, text, fixedWidth){
14553             if(!shared){
14554                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14555             }
14556             shared.bind(el);
14557             shared.setFixedWidth(fixedWidth || 'auto');
14558             return shared.getSize(text);
14559         },
14560
14561         /**
14562          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14563          * the overhead of multiple calls to initialize the style properties on each measurement.
14564          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14565          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14566          * in order to accurately measure the text height
14567          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14568          */
14569         createInstance : function(el, fixedWidth){
14570             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14571         }
14572     };
14573 }();
14574
14575  
14576
14577 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14578     var ml = new Roo.Element(document.createElement('div'));
14579     document.body.appendChild(ml.dom);
14580     ml.position('absolute');
14581     ml.setLeftTop(-1000, -1000);
14582     ml.hide();
14583
14584     if(fixedWidth){
14585         ml.setWidth(fixedWidth);
14586     }
14587      
14588     var instance = {
14589         /**
14590          * Returns the size of the specified text based on the internal element's style and width properties
14591          * @memberOf Roo.util.TextMetrics.Instance#
14592          * @param {String} text The text to measure
14593          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14594          */
14595         getSize : function(text){
14596             ml.update(text);
14597             var s = ml.getSize();
14598             ml.update('');
14599             return s;
14600         },
14601
14602         /**
14603          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14604          * that can affect the size of the rendered text
14605          * @memberOf Roo.util.TextMetrics.Instance#
14606          * @param {String/HTMLElement} el The element, dom node or id
14607          */
14608         bind : function(el){
14609             ml.setStyle(
14610                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14611             );
14612         },
14613
14614         /**
14615          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14616          * to set a fixed width in order to accurately measure the text height.
14617          * @memberOf Roo.util.TextMetrics.Instance#
14618          * @param {Number} width The width to set on the element
14619          */
14620         setFixedWidth : function(width){
14621             ml.setWidth(width);
14622         },
14623
14624         /**
14625          * Returns the measured width of the specified text
14626          * @memberOf Roo.util.TextMetrics.Instance#
14627          * @param {String} text The text to measure
14628          * @return {Number} width The width in pixels
14629          */
14630         getWidth : function(text){
14631             ml.dom.style.width = 'auto';
14632             return this.getSize(text).width;
14633         },
14634
14635         /**
14636          * Returns the measured height of the specified text.  For multiline text, be sure to call
14637          * {@link #setFixedWidth} if necessary.
14638          * @memberOf Roo.util.TextMetrics.Instance#
14639          * @param {String} text The text to measure
14640          * @return {Number} height The height in pixels
14641          */
14642         getHeight : function(text){
14643             return this.getSize(text).height;
14644         }
14645     };
14646
14647     instance.bind(bindTo);
14648
14649     return instance;
14650 };
14651
14652 // backwards compat
14653 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14654  * Based on:
14655  * Ext JS Library 1.1.1
14656  * Copyright(c) 2006-2007, Ext JS, LLC.
14657  *
14658  * Originally Released Under LGPL - original licence link has changed is not relivant.
14659  *
14660  * Fork - LGPL
14661  * <script type="text/javascript">
14662  */
14663
14664 /**
14665  * @class Roo.state.Provider
14666  * Abstract base class for state provider implementations. This class provides methods
14667  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14668  * Provider interface.
14669  */
14670 Roo.state.Provider = function(){
14671     /**
14672      * @event statechange
14673      * Fires when a state change occurs.
14674      * @param {Provider} this This state provider
14675      * @param {String} key The state key which was changed
14676      * @param {String} value The encoded value for the state
14677      */
14678     this.addEvents({
14679         "statechange": true
14680     });
14681     this.state = {};
14682     Roo.state.Provider.superclass.constructor.call(this);
14683 };
14684 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14685     /**
14686      * Returns the current value for a key
14687      * @param {String} name The key name
14688      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14689      * @return {Mixed} The state data
14690      */
14691     get : function(name, defaultValue){
14692         return typeof this.state[name] == "undefined" ?
14693             defaultValue : this.state[name];
14694     },
14695     
14696     /**
14697      * Clears a value from the state
14698      * @param {String} name The key name
14699      */
14700     clear : function(name){
14701         delete this.state[name];
14702         this.fireEvent("statechange", this, name, null);
14703     },
14704     
14705     /**
14706      * Sets the value for a key
14707      * @param {String} name The key name
14708      * @param {Mixed} value The value to set
14709      */
14710     set : function(name, value){
14711         this.state[name] = value;
14712         this.fireEvent("statechange", this, name, value);
14713     },
14714     
14715     /**
14716      * Decodes a string previously encoded with {@link #encodeValue}.
14717      * @param {String} value The value to decode
14718      * @return {Mixed} The decoded value
14719      */
14720     decodeValue : function(cookie){
14721         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14722         var matches = re.exec(unescape(cookie));
14723         if(!matches || !matches[1]) return; // non state cookie
14724         var type = matches[1];
14725         var v = matches[2];
14726         switch(type){
14727             case "n":
14728                 return parseFloat(v);
14729             case "d":
14730                 return new Date(Date.parse(v));
14731             case "b":
14732                 return (v == "1");
14733             case "a":
14734                 var all = [];
14735                 var values = v.split("^");
14736                 for(var i = 0, len = values.length; i < len; i++){
14737                     all.push(this.decodeValue(values[i]));
14738                 }
14739                 return all;
14740            case "o":
14741                 var all = {};
14742                 var values = v.split("^");
14743                 for(var i = 0, len = values.length; i < len; i++){
14744                     var kv = values[i].split("=");
14745                     all[kv[0]] = this.decodeValue(kv[1]);
14746                 }
14747                 return all;
14748            default:
14749                 return v;
14750         }
14751     },
14752     
14753     /**
14754      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14755      * @param {Mixed} value The value to encode
14756      * @return {String} The encoded value
14757      */
14758     encodeValue : function(v){
14759         var enc;
14760         if(typeof v == "number"){
14761             enc = "n:" + v;
14762         }else if(typeof v == "boolean"){
14763             enc = "b:" + (v ? "1" : "0");
14764         }else if(v instanceof Date){
14765             enc = "d:" + v.toGMTString();
14766         }else if(v instanceof Array){
14767             var flat = "";
14768             for(var i = 0, len = v.length; i < len; i++){
14769                 flat += this.encodeValue(v[i]);
14770                 if(i != len-1) flat += "^";
14771             }
14772             enc = "a:" + flat;
14773         }else if(typeof v == "object"){
14774             var flat = "";
14775             for(var key in v){
14776                 if(typeof v[key] != "function"){
14777                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14778                 }
14779             }
14780             enc = "o:" + flat.substring(0, flat.length-1);
14781         }else{
14782             enc = "s:" + v;
14783         }
14784         return escape(enc);        
14785     }
14786 });
14787
14788 /*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798 /**
14799  * @class Roo.state.Manager
14800  * This is the global state manager. By default all components that are "state aware" check this class
14801  * for state information if you don't pass them a custom state provider. In order for this class
14802  * to be useful, it must be initialized with a provider when your application initializes.
14803  <pre><code>
14804 // in your initialization function
14805 init : function(){
14806    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14807    ...
14808    // supposed you have a {@link Roo.BorderLayout}
14809    var layout = new Roo.BorderLayout(...);
14810    layout.restoreState();
14811    // or a {Roo.BasicDialog}
14812    var dialog = new Roo.BasicDialog(...);
14813    dialog.restoreState();
14814  </code></pre>
14815  * @singleton
14816  */
14817 Roo.state.Manager = function(){
14818     var provider = new Roo.state.Provider();
14819     
14820     return {
14821         /**
14822          * Configures the default state provider for your application
14823          * @param {Provider} stateProvider The state provider to set
14824          */
14825         setProvider : function(stateProvider){
14826             provider = stateProvider;
14827         },
14828         
14829         /**
14830          * Returns the current value for a key
14831          * @param {String} name The key name
14832          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14833          * @return {Mixed} The state data
14834          */
14835         get : function(key, defaultValue){
14836             return provider.get(key, defaultValue);
14837         },
14838         
14839         /**
14840          * Sets the value for a key
14841          * @param {String} name The key name
14842          * @param {Mixed} value The state data
14843          */
14844          set : function(key, value){
14845             provider.set(key, value);
14846         },
14847         
14848         /**
14849          * Clears a value from the state
14850          * @param {String} name The key name
14851          */
14852         clear : function(key){
14853             provider.clear(key);
14854         },
14855         
14856         /**
14857          * Gets the currently configured state provider
14858          * @return {Provider} The state provider
14859          */
14860         getProvider : function(){
14861             return provider;
14862         }
14863     };
14864 }();
14865 /*
14866  * Based on:
14867  * Ext JS Library 1.1.1
14868  * Copyright(c) 2006-2007, Ext JS, LLC.
14869  *
14870  * Originally Released Under LGPL - original licence link has changed is not relivant.
14871  *
14872  * Fork - LGPL
14873  * <script type="text/javascript">
14874  */
14875 /**
14876  * @class Roo.state.CookieProvider
14877  * @extends Roo.state.Provider
14878  * The default Provider implementation which saves state via cookies.
14879  * <br />Usage:
14880  <pre><code>
14881    var cp = new Roo.state.CookieProvider({
14882        path: "/cgi-bin/",
14883        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14884        domain: "roojs.com"
14885    })
14886    Roo.state.Manager.setProvider(cp);
14887  </code></pre>
14888  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14889  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14890  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14891  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14892  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14893  * domain the page is running on including the 'www' like 'www.roojs.com')
14894  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14895  * @constructor
14896  * Create a new CookieProvider
14897  * @param {Object} config The configuration object
14898  */
14899 Roo.state.CookieProvider = function(config){
14900     Roo.state.CookieProvider.superclass.constructor.call(this);
14901     this.path = "/";
14902     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14903     this.domain = null;
14904     this.secure = false;
14905     Roo.apply(this, config);
14906     this.state = this.readCookies();
14907 };
14908
14909 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14910     // private
14911     set : function(name, value){
14912         if(typeof value == "undefined" || value === null){
14913             this.clear(name);
14914             return;
14915         }
14916         this.setCookie(name, value);
14917         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14918     },
14919
14920     // private
14921     clear : function(name){
14922         this.clearCookie(name);
14923         Roo.state.CookieProvider.superclass.clear.call(this, name);
14924     },
14925
14926     // private
14927     readCookies : function(){
14928         var cookies = {};
14929         var c = document.cookie + ";";
14930         var re = /\s?(.*?)=(.*?);/g;
14931         var matches;
14932         while((matches = re.exec(c)) != null){
14933             var name = matches[1];
14934             var value = matches[2];
14935             if(name && name.substring(0,3) == "ys-"){
14936                 cookies[name.substr(3)] = this.decodeValue(value);
14937             }
14938         }
14939         return cookies;
14940     },
14941
14942     // private
14943     setCookie : function(name, value){
14944         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14945            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14946            ((this.path == null) ? "" : ("; path=" + this.path)) +
14947            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14948            ((this.secure == true) ? "; secure" : "");
14949     },
14950
14951     // private
14952     clearCookie : function(name){
14953         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14954            ((this.path == null) ? "" : ("; path=" + this.path)) +
14955            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14956            ((this.secure == true) ? "; secure" : "");
14957     }
14958 });/*
14959  * Based on:
14960  * Ext JS Library 1.1.1
14961  * Copyright(c) 2006-2007, Ext JS, LLC.
14962  *
14963  * Originally Released Under LGPL - original licence link has changed is not relivant.
14964  *
14965  * Fork - LGPL
14966  * <script type="text/javascript">
14967  */
14968  
14969
14970 /**
14971  * @class Roo.ComponentMgr
14972  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14973  * @singleton
14974  */
14975 Roo.ComponentMgr = function(){
14976     var all = new Roo.util.MixedCollection();
14977
14978     return {
14979         /**
14980          * Registers a component.
14981          * @param {Roo.Component} c The component
14982          */
14983         register : function(c){
14984             all.add(c);
14985         },
14986
14987         /**
14988          * Unregisters a component.
14989          * @param {Roo.Component} c The component
14990          */
14991         unregister : function(c){
14992             all.remove(c);
14993         },
14994
14995         /**
14996          * Returns a component by id
14997          * @param {String} id The component id
14998          */
14999         get : function(id){
15000             return all.get(id);
15001         },
15002
15003         /**
15004          * Registers a function that will be called when a specified component is added to ComponentMgr
15005          * @param {String} id The component id
15006          * @param {Funtction} fn The callback function
15007          * @param {Object} scope The scope of the callback
15008          */
15009         onAvailable : function(id, fn, scope){
15010             all.on("add", function(index, o){
15011                 if(o.id == id){
15012                     fn.call(scope || o, o);
15013                     all.un("add", fn, scope);
15014                 }
15015             });
15016         }
15017     };
15018 }();/*
15019  * Based on:
15020  * Ext JS Library 1.1.1
15021  * Copyright(c) 2006-2007, Ext JS, LLC.
15022  *
15023  * Originally Released Under LGPL - original licence link has changed is not relivant.
15024  *
15025  * Fork - LGPL
15026  * <script type="text/javascript">
15027  */
15028  
15029 /**
15030  * @class Roo.Component
15031  * @extends Roo.util.Observable
15032  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15033  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15034  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15035  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15036  * All visual components (widgets) that require rendering into a layout should subclass Component.
15037  * @constructor
15038  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15039  * 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
15040  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15041  */
15042 Roo.Component = function(config){
15043     config = config || {};
15044     if(config.tagName || config.dom || typeof config == "string"){ // element object
15045         config = {el: config, id: config.id || config};
15046     }
15047     this.initialConfig = config;
15048
15049     Roo.apply(this, config);
15050     this.addEvents({
15051         /**
15052          * @event disable
15053          * Fires after the component is disabled.
15054              * @param {Roo.Component} this
15055              */
15056         disable : true,
15057         /**
15058          * @event enable
15059          * Fires after the component is enabled.
15060              * @param {Roo.Component} this
15061              */
15062         enable : true,
15063         /**
15064          * @event beforeshow
15065          * Fires before the component is shown.  Return false to stop the show.
15066              * @param {Roo.Component} this
15067              */
15068         beforeshow : true,
15069         /**
15070          * @event show
15071          * Fires after the component is shown.
15072              * @param {Roo.Component} this
15073              */
15074         show : true,
15075         /**
15076          * @event beforehide
15077          * Fires before the component is hidden. Return false to stop the hide.
15078              * @param {Roo.Component} this
15079              */
15080         beforehide : true,
15081         /**
15082          * @event hide
15083          * Fires after the component is hidden.
15084              * @param {Roo.Component} this
15085              */
15086         hide : true,
15087         /**
15088          * @event beforerender
15089          * Fires before the component is rendered. Return false to stop the render.
15090              * @param {Roo.Component} this
15091              */
15092         beforerender : true,
15093         /**
15094          * @event render
15095          * Fires after the component is rendered.
15096              * @param {Roo.Component} this
15097              */
15098         render : true,
15099         /**
15100          * @event beforedestroy
15101          * Fires before the component is destroyed. Return false to stop the destroy.
15102              * @param {Roo.Component} this
15103              */
15104         beforedestroy : true,
15105         /**
15106          * @event destroy
15107          * Fires after the component is destroyed.
15108              * @param {Roo.Component} this
15109              */
15110         destroy : true
15111     });
15112     if(!this.id){
15113         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15114     }
15115     Roo.ComponentMgr.register(this);
15116     Roo.Component.superclass.constructor.call(this);
15117     this.initComponent();
15118     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15119         this.render(this.renderTo);
15120         delete this.renderTo;
15121     }
15122 };
15123
15124 /** @private */
15125 Roo.Component.AUTO_ID = 1000;
15126
15127 Roo.extend(Roo.Component, Roo.util.Observable, {
15128     /**
15129      * @scope Roo.Component.prototype
15130      * @type {Boolean}
15131      * true if this component is hidden. Read-only.
15132      */
15133     hidden : false,
15134     /**
15135      * @type {Boolean}
15136      * true if this component is disabled. Read-only.
15137      */
15138     disabled : false,
15139     /**
15140      * @type {Boolean}
15141      * true if this component has been rendered. Read-only.
15142      */
15143     rendered : false,
15144     
15145     /** @cfg {String} disableClass
15146      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15147      */
15148     disabledClass : "x-item-disabled",
15149         /** @cfg {Boolean} allowDomMove
15150          * Whether the component can move the Dom node when rendering (defaults to true).
15151          */
15152     allowDomMove : true,
15153     /** @cfg {String} hideMode
15154      * How this component should hidden. Supported values are
15155      * "visibility" (css visibility), "offsets" (negative offset position) and
15156      * "display" (css display) - defaults to "display".
15157      */
15158     hideMode: 'display',
15159
15160     /** @private */
15161     ctype : "Roo.Component",
15162
15163     /**
15164      * @cfg {String} actionMode 
15165      * which property holds the element that used for  hide() / show() / disable() / enable()
15166      * default is 'el' 
15167      */
15168     actionMode : "el",
15169
15170     /** @private */
15171     getActionEl : function(){
15172         return this[this.actionMode];
15173     },
15174
15175     initComponent : Roo.emptyFn,
15176     /**
15177      * If this is a lazy rendering component, render it to its container element.
15178      * @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.
15179      */
15180     render : function(container, position){
15181         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15182             if(!container && this.el){
15183                 this.el = Roo.get(this.el);
15184                 container = this.el.dom.parentNode;
15185                 this.allowDomMove = false;
15186             }
15187             this.container = Roo.get(container);
15188             this.rendered = true;
15189             if(position !== undefined){
15190                 if(typeof position == 'number'){
15191                     position = this.container.dom.childNodes[position];
15192                 }else{
15193                     position = Roo.getDom(position);
15194                 }
15195             }
15196             this.onRender(this.container, position || null);
15197             if(this.cls){
15198                 this.el.addClass(this.cls);
15199                 delete this.cls;
15200             }
15201             if(this.style){
15202                 this.el.applyStyles(this.style);
15203                 delete this.style;
15204             }
15205             this.fireEvent("render", this);
15206             this.afterRender(this.container);
15207             if(this.hidden){
15208                 this.hide();
15209             }
15210             if(this.disabled){
15211                 this.disable();
15212             }
15213         }
15214         return this;
15215     },
15216
15217     /** @private */
15218     // default function is not really useful
15219     onRender : function(ct, position){
15220         if(this.el){
15221             this.el = Roo.get(this.el);
15222             if(this.allowDomMove !== false){
15223                 ct.dom.insertBefore(this.el.dom, position);
15224             }
15225         }
15226     },
15227
15228     /** @private */
15229     getAutoCreate : function(){
15230         var cfg = typeof this.autoCreate == "object" ?
15231                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15232         if(this.id && !cfg.id){
15233             cfg.id = this.id;
15234         }
15235         return cfg;
15236     },
15237
15238     /** @private */
15239     afterRender : Roo.emptyFn,
15240
15241     /**
15242      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15243      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15244      */
15245     destroy : function(){
15246         if(this.fireEvent("beforedestroy", this) !== false){
15247             this.purgeListeners();
15248             this.beforeDestroy();
15249             if(this.rendered){
15250                 this.el.removeAllListeners();
15251                 this.el.remove();
15252                 if(this.actionMode == "container"){
15253                     this.container.remove();
15254                 }
15255             }
15256             this.onDestroy();
15257             Roo.ComponentMgr.unregister(this);
15258             this.fireEvent("destroy", this);
15259         }
15260     },
15261
15262         /** @private */
15263     beforeDestroy : function(){
15264
15265     },
15266
15267         /** @private */
15268         onDestroy : function(){
15269
15270     },
15271
15272     /**
15273      * Returns the underlying {@link Roo.Element}.
15274      * @return {Roo.Element} The element
15275      */
15276     getEl : function(){
15277         return this.el;
15278     },
15279
15280     /**
15281      * Returns the id of this component.
15282      * @return {String}
15283      */
15284     getId : function(){
15285         return this.id;
15286     },
15287
15288     /**
15289      * Try to focus this component.
15290      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15291      * @return {Roo.Component} this
15292      */
15293     focus : function(selectText){
15294         if(this.rendered){
15295             this.el.focus();
15296             if(selectText === true){
15297                 this.el.dom.select();
15298             }
15299         }
15300         return this;
15301     },
15302
15303     /** @private */
15304     blur : function(){
15305         if(this.rendered){
15306             this.el.blur();
15307         }
15308         return this;
15309     },
15310
15311     /**
15312      * Disable this component.
15313      * @return {Roo.Component} this
15314      */
15315     disable : function(){
15316         if(this.rendered){
15317             this.onDisable();
15318         }
15319         this.disabled = true;
15320         this.fireEvent("disable", this);
15321         return this;
15322     },
15323
15324         // private
15325     onDisable : function(){
15326         this.getActionEl().addClass(this.disabledClass);
15327         this.el.dom.disabled = true;
15328     },
15329
15330     /**
15331      * Enable this component.
15332      * @return {Roo.Component} this
15333      */
15334     enable : function(){
15335         if(this.rendered){
15336             this.onEnable();
15337         }
15338         this.disabled = false;
15339         this.fireEvent("enable", this);
15340         return this;
15341     },
15342
15343         // private
15344     onEnable : function(){
15345         this.getActionEl().removeClass(this.disabledClass);
15346         this.el.dom.disabled = false;
15347     },
15348
15349     /**
15350      * Convenience function for setting disabled/enabled by boolean.
15351      * @param {Boolean} disabled
15352      */
15353     setDisabled : function(disabled){
15354         this[disabled ? "disable" : "enable"]();
15355     },
15356
15357     /**
15358      * Show this component.
15359      * @return {Roo.Component} this
15360      */
15361     show: function(){
15362         if(this.fireEvent("beforeshow", this) !== false){
15363             this.hidden = false;
15364             if(this.rendered){
15365                 this.onShow();
15366             }
15367             this.fireEvent("show", this);
15368         }
15369         return this;
15370     },
15371
15372     // private
15373     onShow : function(){
15374         var ae = this.getActionEl();
15375         if(this.hideMode == 'visibility'){
15376             ae.dom.style.visibility = "visible";
15377         }else if(this.hideMode == 'offsets'){
15378             ae.removeClass('x-hidden');
15379         }else{
15380             ae.dom.style.display = "";
15381         }
15382     },
15383
15384     /**
15385      * Hide this component.
15386      * @return {Roo.Component} this
15387      */
15388     hide: function(){
15389         if(this.fireEvent("beforehide", this) !== false){
15390             this.hidden = true;
15391             if(this.rendered){
15392                 this.onHide();
15393             }
15394             this.fireEvent("hide", this);
15395         }
15396         return this;
15397     },
15398
15399     // private
15400     onHide : function(){
15401         var ae = this.getActionEl();
15402         if(this.hideMode == 'visibility'){
15403             ae.dom.style.visibility = "hidden";
15404         }else if(this.hideMode == 'offsets'){
15405             ae.addClass('x-hidden');
15406         }else{
15407             ae.dom.style.display = "none";
15408         }
15409     },
15410
15411     /**
15412      * Convenience function to hide or show this component by boolean.
15413      * @param {Boolean} visible True to show, false to hide
15414      * @return {Roo.Component} this
15415      */
15416     setVisible: function(visible){
15417         if(visible) {
15418             this.show();
15419         }else{
15420             this.hide();
15421         }
15422         return this;
15423     },
15424
15425     /**
15426      * Returns true if this component is visible.
15427      */
15428     isVisible : function(){
15429         return this.getActionEl().isVisible();
15430     },
15431
15432     cloneConfig : function(overrides){
15433         overrides = overrides || {};
15434         var id = overrides.id || Roo.id();
15435         var cfg = Roo.applyIf(overrides, this.initialConfig);
15436         cfg.id = id; // prevent dup id
15437         return new this.constructor(cfg);
15438     }
15439 });/*
15440  * Based on:
15441  * Ext JS Library 1.1.1
15442  * Copyright(c) 2006-2007, Ext JS, LLC.
15443  *
15444  * Originally Released Under LGPL - original licence link has changed is not relivant.
15445  *
15446  * Fork - LGPL
15447  * <script type="text/javascript">
15448  */
15449
15450 /**
15451  * @class Roo.BoxComponent
15452  * @extends Roo.Component
15453  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15454  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15455  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15456  * layout containers.
15457  * @constructor
15458  * @param {Roo.Element/String/Object} config The configuration options.
15459  */
15460 Roo.BoxComponent = function(config){
15461     Roo.Component.call(this, config);
15462     this.addEvents({
15463         /**
15464          * @event resize
15465          * Fires after the component is resized.
15466              * @param {Roo.Component} this
15467              * @param {Number} adjWidth The box-adjusted width that was set
15468              * @param {Number} adjHeight The box-adjusted height that was set
15469              * @param {Number} rawWidth The width that was originally specified
15470              * @param {Number} rawHeight The height that was originally specified
15471              */
15472         resize : true,
15473         /**
15474          * @event move
15475          * Fires after the component is moved.
15476              * @param {Roo.Component} this
15477              * @param {Number} x The new x position
15478              * @param {Number} y The new y position
15479              */
15480         move : true
15481     });
15482 };
15483
15484 Roo.extend(Roo.BoxComponent, Roo.Component, {
15485     // private, set in afterRender to signify that the component has been rendered
15486     boxReady : false,
15487     // private, used to defer height settings to subclasses
15488     deferHeight: false,
15489     /** @cfg {Number} width
15490      * width (optional) size of component
15491      */
15492      /** @cfg {Number} height
15493      * height (optional) size of component
15494      */
15495      
15496     /**
15497      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15498      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15499      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15500      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15501      * @return {Roo.BoxComponent} this
15502      */
15503     setSize : function(w, h){
15504         // support for standard size objects
15505         if(typeof w == 'object'){
15506             h = w.height;
15507             w = w.width;
15508         }
15509         // not rendered
15510         if(!this.boxReady){
15511             this.width = w;
15512             this.height = h;
15513             return this;
15514         }
15515
15516         // prevent recalcs when not needed
15517         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15518             return this;
15519         }
15520         this.lastSize = {width: w, height: h};
15521
15522         var adj = this.adjustSize(w, h);
15523         var aw = adj.width, ah = adj.height;
15524         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15525             var rz = this.getResizeEl();
15526             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15527                 rz.setSize(aw, ah);
15528             }else if(!this.deferHeight && ah !== undefined){
15529                 rz.setHeight(ah);
15530             }else if(aw !== undefined){
15531                 rz.setWidth(aw);
15532             }
15533             this.onResize(aw, ah, w, h);
15534             this.fireEvent('resize', this, aw, ah, w, h);
15535         }
15536         return this;
15537     },
15538
15539     /**
15540      * Gets the current size of the component's underlying element.
15541      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15542      */
15543     getSize : function(){
15544         return this.el.getSize();
15545     },
15546
15547     /**
15548      * Gets the current XY position of the component's underlying element.
15549      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15550      * @return {Array} The XY position of the element (e.g., [100, 200])
15551      */
15552     getPosition : function(local){
15553         if(local === true){
15554             return [this.el.getLeft(true), this.el.getTop(true)];
15555         }
15556         return this.xy || this.el.getXY();
15557     },
15558
15559     /**
15560      * Gets the current box measurements of the component's underlying element.
15561      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15562      * @returns {Object} box An object in the format {x, y, width, height}
15563      */
15564     getBox : function(local){
15565         var s = this.el.getSize();
15566         if(local){
15567             s.x = this.el.getLeft(true);
15568             s.y = this.el.getTop(true);
15569         }else{
15570             var xy = this.xy || this.el.getXY();
15571             s.x = xy[0];
15572             s.y = xy[1];
15573         }
15574         return s;
15575     },
15576
15577     /**
15578      * Sets the current box measurements of the component's underlying element.
15579      * @param {Object} box An object in the format {x, y, width, height}
15580      * @returns {Roo.BoxComponent} this
15581      */
15582     updateBox : function(box){
15583         this.setSize(box.width, box.height);
15584         this.setPagePosition(box.x, box.y);
15585         return this;
15586     },
15587
15588     // protected
15589     getResizeEl : function(){
15590         return this.resizeEl || this.el;
15591     },
15592
15593     // protected
15594     getPositionEl : function(){
15595         return this.positionEl || this.el;
15596     },
15597
15598     /**
15599      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15600      * This method fires the move event.
15601      * @param {Number} left The new left
15602      * @param {Number} top The new top
15603      * @returns {Roo.BoxComponent} this
15604      */
15605     setPosition : function(x, y){
15606         this.x = x;
15607         this.y = y;
15608         if(!this.boxReady){
15609             return this;
15610         }
15611         var adj = this.adjustPosition(x, y);
15612         var ax = adj.x, ay = adj.y;
15613
15614         var el = this.getPositionEl();
15615         if(ax !== undefined || ay !== undefined){
15616             if(ax !== undefined && ay !== undefined){
15617                 el.setLeftTop(ax, ay);
15618             }else if(ax !== undefined){
15619                 el.setLeft(ax);
15620             }else if(ay !== undefined){
15621                 el.setTop(ay);
15622             }
15623             this.onPosition(ax, ay);
15624             this.fireEvent('move', this, ax, ay);
15625         }
15626         return this;
15627     },
15628
15629     /**
15630      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15631      * This method fires the move event.
15632      * @param {Number} x The new x position
15633      * @param {Number} y The new y position
15634      * @returns {Roo.BoxComponent} this
15635      */
15636     setPagePosition : function(x, y){
15637         this.pageX = x;
15638         this.pageY = y;
15639         if(!this.boxReady){
15640             return;
15641         }
15642         if(x === undefined || y === undefined){ // cannot translate undefined points
15643             return;
15644         }
15645         var p = this.el.translatePoints(x, y);
15646         this.setPosition(p.left, p.top);
15647         return this;
15648     },
15649
15650     // private
15651     onRender : function(ct, position){
15652         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15653         if(this.resizeEl){
15654             this.resizeEl = Roo.get(this.resizeEl);
15655         }
15656         if(this.positionEl){
15657             this.positionEl = Roo.get(this.positionEl);
15658         }
15659     },
15660
15661     // private
15662     afterRender : function(){
15663         Roo.BoxComponent.superclass.afterRender.call(this);
15664         this.boxReady = true;
15665         this.setSize(this.width, this.height);
15666         if(this.x || this.y){
15667             this.setPosition(this.x, this.y);
15668         }
15669         if(this.pageX || this.pageY){
15670             this.setPagePosition(this.pageX, this.pageY);
15671         }
15672     },
15673
15674     /**
15675      * Force the component's size to recalculate based on the underlying element's current height and width.
15676      * @returns {Roo.BoxComponent} this
15677      */
15678     syncSize : function(){
15679         delete this.lastSize;
15680         this.setSize(this.el.getWidth(), this.el.getHeight());
15681         return this;
15682     },
15683
15684     /**
15685      * Called after the component is resized, this method is empty by default but can be implemented by any
15686      * subclass that needs to perform custom logic after a resize occurs.
15687      * @param {Number} adjWidth The box-adjusted width that was set
15688      * @param {Number} adjHeight The box-adjusted height that was set
15689      * @param {Number} rawWidth The width that was originally specified
15690      * @param {Number} rawHeight The height that was originally specified
15691      */
15692     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15693
15694     },
15695
15696     /**
15697      * Called after the component is moved, this method is empty by default but can be implemented by any
15698      * subclass that needs to perform custom logic after a move occurs.
15699      * @param {Number} x The new x position
15700      * @param {Number} y The new y position
15701      */
15702     onPosition : function(x, y){
15703
15704     },
15705
15706     // private
15707     adjustSize : function(w, h){
15708         if(this.autoWidth){
15709             w = 'auto';
15710         }
15711         if(this.autoHeight){
15712             h = 'auto';
15713         }
15714         return {width : w, height: h};
15715     },
15716
15717     // private
15718     adjustPosition : function(x, y){
15719         return {x : x, y: y};
15720     }
15721 });/*
15722  * Original code for Roojs - LGPL
15723  * <script type="text/javascript">
15724  */
15725  
15726 /**
15727  * @class Roo.XComponent
15728  * A delayed Element creator...
15729  * Or a way to group chunks of interface together.
15730  * 
15731  * Mypart.xyx = new Roo.XComponent({
15732
15733     parent : 'Mypart.xyz', // empty == document.element.!!
15734     order : '001',
15735     name : 'xxxx'
15736     region : 'xxxx'
15737     disabled : function() {} 
15738      
15739     tree : function() { // return an tree of xtype declared components
15740         var MODULE = this;
15741         return 
15742         {
15743             xtype : 'NestedLayoutPanel',
15744             // technicall
15745         }
15746      ]
15747  *})
15748  *
15749  *
15750  * It can be used to build a big heiracy, with parent etc.
15751  * or you can just use this to render a single compoent to a dom element
15752  * MYPART.render(Roo.Element | String(id) | dom_element )
15753  * 
15754  * @extends Roo.util.Observable
15755  * @constructor
15756  * @param cfg {Object} configuration of component
15757  * 
15758  */
15759 Roo.XComponent = function(cfg) {
15760     Roo.apply(this, cfg);
15761     this.addEvents({ 
15762         /**
15763              * @event built
15764              * Fires when this the componnt is built
15765              * @param {Roo.XComponent} c the component
15766              */
15767         'built' : true
15768         
15769     });
15770     this.region = this.region || 'center'; // default..
15771     Roo.XComponent.register(this);
15772     this.modules = false;
15773     this.el = false; // where the layout goes..
15774     
15775     
15776 }
15777 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15778     /**
15779      * @property el
15780      * The created element (with Roo.factory())
15781      * @type {Roo.Layout}
15782      */
15783     el  : false,
15784     
15785     /**
15786      * @property el
15787      * for BC  - use el in new code
15788      * @type {Roo.Layout}
15789      */
15790     panel : false,
15791     
15792     /**
15793      * @property layout
15794      * for BC  - use el in new code
15795      * @type {Roo.Layout}
15796      */
15797     layout : false,
15798     
15799      /**
15800      * @cfg {Function|boolean} disabled
15801      * If this module is disabled by some rule, return true from the funtion
15802      */
15803     disabled : false,
15804     
15805     /**
15806      * @cfg {String} parent 
15807      * Name of parent element which it get xtype added to..
15808      */
15809     parent: false,
15810     
15811     /**
15812      * @cfg {String} order
15813      * Used to set the order in which elements are created (usefull for multiple tabs)
15814      */
15815     
15816     order : false,
15817     /**
15818      * @cfg {String} name
15819      * String to display while loading.
15820      */
15821     name : false,
15822     /**
15823      * @cfg {String} region
15824      * Region to render component to (defaults to center)
15825      */
15826     region : 'center',
15827     
15828     /**
15829      * @cfg {Array} items
15830      * A single item array - the first element is the root of the tree..
15831      * It's done this way to stay compatible with the Xtype system...
15832      */
15833     items : false,
15834     
15835     /**
15836      * @property _tree
15837      * The method that retuns the tree of parts that make up this compoennt 
15838      * @type {function}
15839      */
15840     _tree  : false,
15841     
15842      /**
15843      * render
15844      * render element to dom or tree
15845      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15846      */
15847     
15848     render : function(el)
15849     {
15850         
15851         el = el || false;
15852         var hp = this.parent ? 1 : 0;
15853         
15854         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15855             // if parent is a '#.....' string, then let's use that..
15856             var ename = this.parent.substr(1)
15857             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15858             el = Roo.get(ename);
15859             if (!el && !this.parent) {
15860                 Roo.log("Warning - element can not be found :#" + ename );
15861                 return;
15862             }
15863         }
15864         
15865         
15866         if (!this.parent) {
15867             
15868             el = el ? Roo.get(el) : false;      
15869             
15870             // it's a top level one..
15871             this.parent =  {
15872                 el : new Roo.BorderLayout(el || document.body, {
15873                 
15874                      center: {
15875                          titlebar: false,
15876                          autoScroll:false,
15877                          closeOnTab: true,
15878                          tabPosition: 'top',
15879                           //resizeTabs: true,
15880                          alwaysShowTabs: el && hp? false :  true,
15881                          hideTabs: el || !hp ? true :  false,
15882                          minTabWidth: 140
15883                      }
15884                  })
15885             }
15886         }
15887         
15888                 if (!this.parent.el) {
15889                         // probably an old style ctor, which has been disabled.
15890                         return;
15891                         
15892                 }
15893                 // The 'tree' method is  '_tree now' 
15894             
15895         var tree = this._tree ? this._tree() : this.tree();
15896         tree.region = tree.region || this.region;
15897         if (this.parent.el === true) {
15898             // bootstrap... - body..
15899             this.parent.el = Roo.factory(tree);
15900         }
15901         this.el = this.parent.el.addxtype(tree);
15902         this.fireEvent('built', this);
15903         
15904         this.panel = this.el;
15905         this.layout = this.panel.layout;
15906                 this.parentLayout = this.parent.layout  || false;  
15907          
15908     }
15909     
15910 });
15911
15912 Roo.apply(Roo.XComponent, {
15913     /**
15914      * @property  hideProgress
15915      * true to disable the building progress bar.. usefull on single page renders.
15916      * @type Boolean
15917      */
15918     hideProgress : false,
15919     /**
15920      * @property  buildCompleted
15921      * True when the builder has completed building the interface.
15922      * @type Boolean
15923      */
15924     buildCompleted : false,
15925      
15926     /**
15927      * @property  topModule
15928      * the upper most module - uses document.element as it's constructor.
15929      * @type Object
15930      */
15931      
15932     topModule  : false,
15933       
15934     /**
15935      * @property  modules
15936      * array of modules to be created by registration system.
15937      * @type {Array} of Roo.XComponent
15938      */
15939     
15940     modules : [],
15941     /**
15942      * @property  elmodules
15943      * array of modules to be created by which use #ID 
15944      * @type {Array} of Roo.XComponent
15945      */
15946      
15947     elmodules : [],
15948
15949     
15950     /**
15951      * Register components to be built later.
15952      *
15953      * This solves the following issues
15954      * - Building is not done on page load, but after an authentication process has occured.
15955      * - Interface elements are registered on page load
15956      * - Parent Interface elements may not be loaded before child, so this handles that..
15957      * 
15958      *
15959      * example:
15960      * 
15961      * MyApp.register({
15962           order : '000001',
15963           module : 'Pman.Tab.projectMgr',
15964           region : 'center',
15965           parent : 'Pman.layout',
15966           disabled : false,  // or use a function..
15967         })
15968      
15969      * * @param {Object} details about module
15970      */
15971     register : function(obj) {
15972                 
15973         Roo.XComponent.event.fireEvent('register', obj);
15974         switch(typeof(obj.disabled) ) {
15975                 
15976             case 'undefined':
15977                 break;
15978             
15979             case 'function':
15980                 if ( obj.disabled() ) {
15981                         return;
15982                 }
15983                 break;
15984             
15985             default:
15986                 if (obj.disabled) {
15987                         return;
15988                 }
15989                 break;
15990         }
15991                 
15992         this.modules.push(obj);
15993          
15994     },
15995     /**
15996      * convert a string to an object..
15997      * eg. 'AAA.BBB' -> finds AAA.BBB
15998
15999      */
16000     
16001     toObject : function(str)
16002     {
16003         if (!str || typeof(str) == 'object') {
16004             return str;
16005         }
16006         if (str.substring(0,1) == '#') {
16007             return str;
16008         }
16009
16010         var ar = str.split('.');
16011         var rt, o;
16012         rt = ar.shift();
16013             /** eval:var:o */
16014         try {
16015             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16016         } catch (e) {
16017             throw "Module not found : " + str;
16018         }
16019         
16020         if (o === false) {
16021             throw "Module not found : " + str;
16022         }
16023         Roo.each(ar, function(e) {
16024             if (typeof(o[e]) == 'undefined') {
16025                 throw "Module not found : " + str;
16026             }
16027             o = o[e];
16028         });
16029         
16030         return o;
16031         
16032     },
16033     
16034     
16035     /**
16036      * move modules into their correct place in the tree..
16037      * 
16038      */
16039     preBuild : function ()
16040     {
16041         var _t = this;
16042         Roo.each(this.modules , function (obj)
16043         {
16044             Roo.XComponent.event.fireEvent('beforebuild', obj);
16045             
16046             var opar = obj.parent;
16047             try { 
16048                 obj.parent = this.toObject(opar);
16049             } catch(e) {
16050                 Roo.log("parent:toObject failed: " + e.toString());
16051                 return;
16052             }
16053             
16054             if (!obj.parent) {
16055                 Roo.debug && Roo.log("GOT top level module");
16056                 Roo.debug && Roo.log(obj);
16057                 obj.modules = new Roo.util.MixedCollection(false, 
16058                     function(o) { return o.order + '' }
16059                 );
16060                 this.topModule = obj;
16061                 return;
16062             }
16063                         // parent is a string (usually a dom element name..)
16064             if (typeof(obj.parent) == 'string') {
16065                 this.elmodules.push(obj);
16066                 return;
16067             }
16068             if (obj.parent.constructor != Roo.XComponent) {
16069                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16070             }
16071             if (!obj.parent.modules) {
16072                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16073                     function(o) { return o.order + '' }
16074                 );
16075             }
16076             if (obj.parent.disabled) {
16077                 obj.disabled = true;
16078             }
16079             obj.parent.modules.add(obj);
16080         }, this);
16081     },
16082     
16083      /**
16084      * make a list of modules to build.
16085      * @return {Array} list of modules. 
16086      */ 
16087     
16088     buildOrder : function()
16089     {
16090         var _this = this;
16091         var cmp = function(a,b) {   
16092             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16093         };
16094         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16095             throw "No top level modules to build";
16096         }
16097         
16098         // make a flat list in order of modules to build.
16099         var mods = this.topModule ? [ this.topModule ] : [];
16100                 
16101         
16102         // elmodules (is a list of DOM based modules )
16103         Roo.each(this.elmodules, function(e) {
16104             mods.push(e);
16105             if (!this.topModule &&
16106                 typeof(e.parent) == 'string' &&
16107                 e.parent.substring(0,1) == '#' &&
16108                 Roo.get(e.parent.substr(1))
16109                ) {
16110                 
16111                 _this.topModule = e;
16112             }
16113             
16114         });
16115
16116         
16117         // add modules to their parents..
16118         var addMod = function(m) {
16119             Roo.debug && Roo.log("build Order: add: " + m.name);
16120                 
16121             mods.push(m);
16122             if (m.modules && !m.disabled) {
16123                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16124                 m.modules.keySort('ASC',  cmp );
16125                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16126     
16127                 m.modules.each(addMod);
16128             } else {
16129                 Roo.debug && Roo.log("build Order: no child modules");
16130             }
16131             // not sure if this is used any more..
16132             if (m.finalize) {
16133                 m.finalize.name = m.name + " (clean up) ";
16134                 mods.push(m.finalize);
16135             }
16136             
16137         }
16138         if (this.topModule && this.topModule.modules) { 
16139             this.topModule.modules.keySort('ASC',  cmp );
16140             this.topModule.modules.each(addMod);
16141         } 
16142         return mods;
16143     },
16144     
16145      /**
16146      * Build the registered modules.
16147      * @param {Object} parent element.
16148      * @param {Function} optional method to call after module has been added.
16149      * 
16150      */ 
16151    
16152     build : function() 
16153     {
16154         
16155         this.preBuild();
16156         var mods = this.buildOrder();
16157       
16158         //this.allmods = mods;
16159         //Roo.debug && Roo.log(mods);
16160         //return;
16161         if (!mods.length) { // should not happen
16162             throw "NO modules!!!";
16163         }
16164         
16165         
16166         var msg = "Building Interface...";
16167         // flash it up as modal - so we store the mask!?
16168         if (!this.hideProgress && Roo.MessageBox) {
16169             Roo.MessageBox.show({ title: 'loading' });
16170             Roo.MessageBox.show({
16171                title: "Please wait...",
16172                msg: msg,
16173                width:450,
16174                progress:true,
16175                closable:false,
16176                modal: false
16177               
16178             });
16179         }
16180         var total = mods.length;
16181         
16182         var _this = this;
16183         var progressRun = function() {
16184             if (!mods.length) {
16185                 Roo.debug && Roo.log('hide?');
16186                 if (!this.hideProgress && Roo.MessageBox) {
16187                     Roo.MessageBox.hide();
16188                 }
16189                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16190                 
16191                 // THE END...
16192                 return false;   
16193             }
16194             
16195             var m = mods.shift();
16196             
16197             
16198             Roo.debug && Roo.log(m);
16199             // not sure if this is supported any more.. - modules that are are just function
16200             if (typeof(m) == 'function') { 
16201                 m.call(this);
16202                 return progressRun.defer(10, _this);
16203             } 
16204             
16205             
16206             msg = "Building Interface " + (total  - mods.length) + 
16207                     " of " + total + 
16208                     (m.name ? (' - ' + m.name) : '');
16209                         Roo.debug && Roo.log(msg);
16210             if (!this.hideProgress &&  Roo.MessageBox) { 
16211                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16212             }
16213             
16214          
16215             // is the module disabled?
16216             var disabled = (typeof(m.disabled) == 'function') ?
16217                 m.disabled.call(m.module.disabled) : m.disabled;    
16218             
16219             
16220             if (disabled) {
16221                 return progressRun(); // we do not update the display!
16222             }
16223             
16224             // now build 
16225             
16226                         
16227                         
16228             m.render();
16229             // it's 10 on top level, and 1 on others??? why...
16230             return progressRun.defer(10, _this);
16231              
16232         }
16233         progressRun.defer(1, _this);
16234      
16235         
16236         
16237     },
16238         
16239         
16240         /**
16241          * Event Object.
16242          *
16243          *
16244          */
16245         event: false, 
16246     /**
16247          * wrapper for event.on - aliased later..  
16248          * Typically use to register a event handler for register:
16249          *
16250          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16251          *
16252          */
16253     on : false
16254    
16255     
16256     
16257 });
16258
16259 Roo.XComponent.event = new Roo.util.Observable({
16260                 events : { 
16261                         /**
16262                          * @event register
16263                          * Fires when an Component is registered,
16264                          * set the disable property on the Component to stop registration.
16265                          * @param {Roo.XComponent} c the component being registerd.
16266                          * 
16267                          */
16268                         'register' : true,
16269             /**
16270                          * @event beforebuild
16271                          * Fires before each Component is built
16272                          * can be used to apply permissions.
16273                          * @param {Roo.XComponent} c the component being registerd.
16274                          * 
16275                          */
16276                         'beforebuild' : true,
16277                         /**
16278                          * @event buildcomplete
16279                          * Fires on the top level element when all elements have been built
16280                          * @param {Roo.XComponent} the top level component.
16281                          */
16282                         'buildcomplete' : true
16283                         
16284                 }
16285 });
16286
16287 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16288  /*
16289  * Based on:
16290  * Ext JS Library 1.1.1
16291  * Copyright(c) 2006-2007, Ext JS, LLC.
16292  *
16293  * Originally Released Under LGPL - original licence link has changed is not relivant.
16294  *
16295  * Fork - LGPL
16296  * <script type="text/javascript">
16297  */
16298
16299
16300
16301 /*
16302  * These classes are derivatives of the similarly named classes in the YUI Library.
16303  * The original license:
16304  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16305  * Code licensed under the BSD License:
16306  * http://developer.yahoo.net/yui/license.txt
16307  */
16308
16309 (function() {
16310
16311 var Event=Roo.EventManager;
16312 var Dom=Roo.lib.Dom;
16313
16314 /**
16315  * @class Roo.dd.DragDrop
16316  * @extends Roo.util.Observable
16317  * Defines the interface and base operation of items that that can be
16318  * dragged or can be drop targets.  It was designed to be extended, overriding
16319  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16320  * Up to three html elements can be associated with a DragDrop instance:
16321  * <ul>
16322  * <li>linked element: the element that is passed into the constructor.
16323  * This is the element which defines the boundaries for interaction with
16324  * other DragDrop objects.</li>
16325  * <li>handle element(s): The drag operation only occurs if the element that
16326  * was clicked matches a handle element.  By default this is the linked
16327  * element, but there are times that you will want only a portion of the
16328  * linked element to initiate the drag operation, and the setHandleElId()
16329  * method provides a way to define this.</li>
16330  * <li>drag element: this represents the element that would be moved along
16331  * with the cursor during a drag operation.  By default, this is the linked
16332  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16333  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16334  * </li>
16335  * </ul>
16336  * This class should not be instantiated until the onload event to ensure that
16337  * the associated elements are available.
16338  * The following would define a DragDrop obj that would interact with any
16339  * other DragDrop obj in the "group1" group:
16340  * <pre>
16341  *  dd = new Roo.dd.DragDrop("div1", "group1");
16342  * </pre>
16343  * Since none of the event handlers have been implemented, nothing would
16344  * actually happen if you were to run the code above.  Normally you would
16345  * override this class or one of the default implementations, but you can
16346  * also override the methods you want on an instance of the class...
16347  * <pre>
16348  *  dd.onDragDrop = function(e, id) {
16349  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16350  *  }
16351  * </pre>
16352  * @constructor
16353  * @param {String} id of the element that is linked to this instance
16354  * @param {String} sGroup the group of related DragDrop objects
16355  * @param {object} config an object containing configurable attributes
16356  *                Valid properties for DragDrop:
16357  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16358  */
16359 Roo.dd.DragDrop = function(id, sGroup, config) {
16360     if (id) {
16361         this.init(id, sGroup, config);
16362     }
16363     
16364 };
16365
16366 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16367
16368     /**
16369      * The id of the element associated with this object.  This is what we
16370      * refer to as the "linked element" because the size and position of
16371      * this element is used to determine when the drag and drop objects have
16372      * interacted.
16373      * @property id
16374      * @type String
16375      */
16376     id: null,
16377
16378     /**
16379      * Configuration attributes passed into the constructor
16380      * @property config
16381      * @type object
16382      */
16383     config: null,
16384
16385     /**
16386      * The id of the element that will be dragged.  By default this is same
16387      * as the linked element , but could be changed to another element. Ex:
16388      * Roo.dd.DDProxy
16389      * @property dragElId
16390      * @type String
16391      * @private
16392      */
16393     dragElId: null,
16394
16395     /**
16396      * the id of the element that initiates the drag operation.  By default
16397      * this is the linked element, but could be changed to be a child of this
16398      * element.  This lets us do things like only starting the drag when the
16399      * header element within the linked html element is clicked.
16400      * @property handleElId
16401      * @type String
16402      * @private
16403      */
16404     handleElId: null,
16405
16406     /**
16407      * An associative array of HTML tags that will be ignored if clicked.
16408      * @property invalidHandleTypes
16409      * @type {string: string}
16410      */
16411     invalidHandleTypes: null,
16412
16413     /**
16414      * An associative array of ids for elements that will be ignored if clicked
16415      * @property invalidHandleIds
16416      * @type {string: string}
16417      */
16418     invalidHandleIds: null,
16419
16420     /**
16421      * An indexted array of css class names for elements that will be ignored
16422      * if clicked.
16423      * @property invalidHandleClasses
16424      * @type string[]
16425      */
16426     invalidHandleClasses: null,
16427
16428     /**
16429      * The linked element's absolute X position at the time the drag was
16430      * started
16431      * @property startPageX
16432      * @type int
16433      * @private
16434      */
16435     startPageX: 0,
16436
16437     /**
16438      * The linked element's absolute X position at the time the drag was
16439      * started
16440      * @property startPageY
16441      * @type int
16442      * @private
16443      */
16444     startPageY: 0,
16445
16446     /**
16447      * The group defines a logical collection of DragDrop objects that are
16448      * related.  Instances only get events when interacting with other
16449      * DragDrop object in the same group.  This lets us define multiple
16450      * groups using a single DragDrop subclass if we want.
16451      * @property groups
16452      * @type {string: string}
16453      */
16454     groups: null,
16455
16456     /**
16457      * Individual drag/drop instances can be locked.  This will prevent
16458      * onmousedown start drag.
16459      * @property locked
16460      * @type boolean
16461      * @private
16462      */
16463     locked: false,
16464
16465     /**
16466      * Lock this instance
16467      * @method lock
16468      */
16469     lock: function() { this.locked = true; },
16470
16471     /**
16472      * Unlock this instace
16473      * @method unlock
16474      */
16475     unlock: function() { this.locked = false; },
16476
16477     /**
16478      * By default, all insances can be a drop target.  This can be disabled by
16479      * setting isTarget to false.
16480      * @method isTarget
16481      * @type boolean
16482      */
16483     isTarget: true,
16484
16485     /**
16486      * The padding configured for this drag and drop object for calculating
16487      * the drop zone intersection with this object.
16488      * @method padding
16489      * @type int[]
16490      */
16491     padding: null,
16492
16493     /**
16494      * Cached reference to the linked element
16495      * @property _domRef
16496      * @private
16497      */
16498     _domRef: null,
16499
16500     /**
16501      * Internal typeof flag
16502      * @property __ygDragDrop
16503      * @private
16504      */
16505     __ygDragDrop: true,
16506
16507     /**
16508      * Set to true when horizontal contraints are applied
16509      * @property constrainX
16510      * @type boolean
16511      * @private
16512      */
16513     constrainX: false,
16514
16515     /**
16516      * Set to true when vertical contraints are applied
16517      * @property constrainY
16518      * @type boolean
16519      * @private
16520      */
16521     constrainY: false,
16522
16523     /**
16524      * The left constraint
16525      * @property minX
16526      * @type int
16527      * @private
16528      */
16529     minX: 0,
16530
16531     /**
16532      * The right constraint
16533      * @property maxX
16534      * @type int
16535      * @private
16536      */
16537     maxX: 0,
16538
16539     /**
16540      * The up constraint
16541      * @property minY
16542      * @type int
16543      * @type int
16544      * @private
16545      */
16546     minY: 0,
16547
16548     /**
16549      * The down constraint
16550      * @property maxY
16551      * @type int
16552      * @private
16553      */
16554     maxY: 0,
16555
16556     /**
16557      * Maintain offsets when we resetconstraints.  Set to true when you want
16558      * the position of the element relative to its parent to stay the same
16559      * when the page changes
16560      *
16561      * @property maintainOffset
16562      * @type boolean
16563      */
16564     maintainOffset: false,
16565
16566     /**
16567      * Array of pixel locations the element will snap to if we specified a
16568      * horizontal graduation/interval.  This array is generated automatically
16569      * when you define a tick interval.
16570      * @property xTicks
16571      * @type int[]
16572      */
16573     xTicks: null,
16574
16575     /**
16576      * Array of pixel locations the element will snap to if we specified a
16577      * vertical graduation/interval.  This array is generated automatically
16578      * when you define a tick interval.
16579      * @property yTicks
16580      * @type int[]
16581      */
16582     yTicks: null,
16583
16584     /**
16585      * By default the drag and drop instance will only respond to the primary
16586      * button click (left button for a right-handed mouse).  Set to true to
16587      * allow drag and drop to start with any mouse click that is propogated
16588      * by the browser
16589      * @property primaryButtonOnly
16590      * @type boolean
16591      */
16592     primaryButtonOnly: true,
16593
16594     /**
16595      * The availabe property is false until the linked dom element is accessible.
16596      * @property available
16597      * @type boolean
16598      */
16599     available: false,
16600
16601     /**
16602      * By default, drags can only be initiated if the mousedown occurs in the
16603      * region the linked element is.  This is done in part to work around a
16604      * bug in some browsers that mis-report the mousedown if the previous
16605      * mouseup happened outside of the window.  This property is set to true
16606      * if outer handles are defined.
16607      *
16608      * @property hasOuterHandles
16609      * @type boolean
16610      * @default false
16611      */
16612     hasOuterHandles: false,
16613
16614     /**
16615      * Code that executes immediately before the startDrag event
16616      * @method b4StartDrag
16617      * @private
16618      */
16619     b4StartDrag: function(x, y) { },
16620
16621     /**
16622      * Abstract method called after a drag/drop object is clicked
16623      * and the drag or mousedown time thresholds have beeen met.
16624      * @method startDrag
16625      * @param {int} X click location
16626      * @param {int} Y click location
16627      */
16628     startDrag: function(x, y) { /* override this */ },
16629
16630     /**
16631      * Code that executes immediately before the onDrag event
16632      * @method b4Drag
16633      * @private
16634      */
16635     b4Drag: function(e) { },
16636
16637     /**
16638      * Abstract method called during the onMouseMove event while dragging an
16639      * object.
16640      * @method onDrag
16641      * @param {Event} e the mousemove event
16642      */
16643     onDrag: function(e) { /* override this */ },
16644
16645     /**
16646      * Abstract method called when this element fist begins hovering over
16647      * another DragDrop obj
16648      * @method onDragEnter
16649      * @param {Event} e the mousemove event
16650      * @param {String|DragDrop[]} id In POINT mode, the element
16651      * id this is hovering over.  In INTERSECT mode, an array of one or more
16652      * dragdrop items being hovered over.
16653      */
16654     onDragEnter: function(e, id) { /* override this */ },
16655
16656     /**
16657      * Code that executes immediately before the onDragOver event
16658      * @method b4DragOver
16659      * @private
16660      */
16661     b4DragOver: function(e) { },
16662
16663     /**
16664      * Abstract method called when this element is hovering over another
16665      * DragDrop obj
16666      * @method onDragOver
16667      * @param {Event} e the mousemove event
16668      * @param {String|DragDrop[]} id In POINT mode, the element
16669      * id this is hovering over.  In INTERSECT mode, an array of dd items
16670      * being hovered over.
16671      */
16672     onDragOver: function(e, id) { /* override this */ },
16673
16674     /**
16675      * Code that executes immediately before the onDragOut event
16676      * @method b4DragOut
16677      * @private
16678      */
16679     b4DragOut: function(e) { },
16680
16681     /**
16682      * Abstract method called when we are no longer hovering over an element
16683      * @method onDragOut
16684      * @param {Event} e the mousemove event
16685      * @param {String|DragDrop[]} id In POINT mode, the element
16686      * id this was hovering over.  In INTERSECT mode, an array of dd items
16687      * that the mouse is no longer over.
16688      */
16689     onDragOut: function(e, id) { /* override this */ },
16690
16691     /**
16692      * Code that executes immediately before the onDragDrop event
16693      * @method b4DragDrop
16694      * @private
16695      */
16696     b4DragDrop: function(e) { },
16697
16698     /**
16699      * Abstract method called when this item is dropped on another DragDrop
16700      * obj
16701      * @method onDragDrop
16702      * @param {Event} e the mouseup event
16703      * @param {String|DragDrop[]} id In POINT mode, the element
16704      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16705      * was dropped on.
16706      */
16707     onDragDrop: function(e, id) { /* override this */ },
16708
16709     /**
16710      * Abstract method called when this item is dropped on an area with no
16711      * drop target
16712      * @method onInvalidDrop
16713      * @param {Event} e the mouseup event
16714      */
16715     onInvalidDrop: function(e) { /* override this */ },
16716
16717     /**
16718      * Code that executes immediately before the endDrag event
16719      * @method b4EndDrag
16720      * @private
16721      */
16722     b4EndDrag: function(e) { },
16723
16724     /**
16725      * Fired when we are done dragging the object
16726      * @method endDrag
16727      * @param {Event} e the mouseup event
16728      */
16729     endDrag: function(e) { /* override this */ },
16730
16731     /**
16732      * Code executed immediately before the onMouseDown event
16733      * @method b4MouseDown
16734      * @param {Event} e the mousedown event
16735      * @private
16736      */
16737     b4MouseDown: function(e) {  },
16738
16739     /**
16740      * Event handler that fires when a drag/drop obj gets a mousedown
16741      * @method onMouseDown
16742      * @param {Event} e the mousedown event
16743      */
16744     onMouseDown: function(e) { /* override this */ },
16745
16746     /**
16747      * Event handler that fires when a drag/drop obj gets a mouseup
16748      * @method onMouseUp
16749      * @param {Event} e the mouseup event
16750      */
16751     onMouseUp: function(e) { /* override this */ },
16752
16753     /**
16754      * Override the onAvailable method to do what is needed after the initial
16755      * position was determined.
16756      * @method onAvailable
16757      */
16758     onAvailable: function () {
16759     },
16760
16761     /*
16762      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16763      * @type Object
16764      */
16765     defaultPadding : {left:0, right:0, top:0, bottom:0},
16766
16767     /*
16768      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16769  *
16770  * Usage:
16771  <pre><code>
16772  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16773                 { dragElId: "existingProxyDiv" });
16774  dd.startDrag = function(){
16775      this.constrainTo("parent-id");
16776  };
16777  </code></pre>
16778  * Or you can initalize it using the {@link Roo.Element} object:
16779  <pre><code>
16780  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16781      startDrag : function(){
16782          this.constrainTo("parent-id");
16783      }
16784  });
16785  </code></pre>
16786      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16787      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16788      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16789      * an object containing the sides to pad. For example: {right:10, bottom:10}
16790      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16791      */
16792     constrainTo : function(constrainTo, pad, inContent){
16793         if(typeof pad == "number"){
16794             pad = {left: pad, right:pad, top:pad, bottom:pad};
16795         }
16796         pad = pad || this.defaultPadding;
16797         var b = Roo.get(this.getEl()).getBox();
16798         var ce = Roo.get(constrainTo);
16799         var s = ce.getScroll();
16800         var c, cd = ce.dom;
16801         if(cd == document.body){
16802             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16803         }else{
16804             xy = ce.getXY();
16805             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16806         }
16807
16808
16809         var topSpace = b.y - c.y;
16810         var leftSpace = b.x - c.x;
16811
16812         this.resetConstraints();
16813         this.setXConstraint(leftSpace - (pad.left||0), // left
16814                 c.width - leftSpace - b.width - (pad.right||0) //right
16815         );
16816         this.setYConstraint(topSpace - (pad.top||0), //top
16817                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16818         );
16819     },
16820
16821     /**
16822      * Returns a reference to the linked element
16823      * @method getEl
16824      * @return {HTMLElement} the html element
16825      */
16826     getEl: function() {
16827         if (!this._domRef) {
16828             this._domRef = Roo.getDom(this.id);
16829         }
16830
16831         return this._domRef;
16832     },
16833
16834     /**
16835      * Returns a reference to the actual element to drag.  By default this is
16836      * the same as the html element, but it can be assigned to another
16837      * element. An example of this can be found in Roo.dd.DDProxy
16838      * @method getDragEl
16839      * @return {HTMLElement} the html element
16840      */
16841     getDragEl: function() {
16842         return Roo.getDom(this.dragElId);
16843     },
16844
16845     /**
16846      * Sets up the DragDrop object.  Must be called in the constructor of any
16847      * Roo.dd.DragDrop subclass
16848      * @method init
16849      * @param id the id of the linked element
16850      * @param {String} sGroup the group of related items
16851      * @param {object} config configuration attributes
16852      */
16853     init: function(id, sGroup, config) {
16854         this.initTarget(id, sGroup, config);
16855         if (!Roo.isTouch) {
16856             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16857         }
16858         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16859         // Event.on(this.id, "selectstart", Event.preventDefault);
16860     },
16861
16862     /**
16863      * Initializes Targeting functionality only... the object does not
16864      * get a mousedown handler.
16865      * @method initTarget
16866      * @param id the id of the linked element
16867      * @param {String} sGroup the group of related items
16868      * @param {object} config configuration attributes
16869      */
16870     initTarget: function(id, sGroup, config) {
16871
16872         // configuration attributes
16873         this.config = config || {};
16874
16875         // create a local reference to the drag and drop manager
16876         this.DDM = Roo.dd.DDM;
16877         // initialize the groups array
16878         this.groups = {};
16879
16880         // assume that we have an element reference instead of an id if the
16881         // parameter is not a string
16882         if (typeof id !== "string") {
16883             id = Roo.id(id);
16884         }
16885
16886         // set the id
16887         this.id = id;
16888
16889         // add to an interaction group
16890         this.addToGroup((sGroup) ? sGroup : "default");
16891
16892         // We don't want to register this as the handle with the manager
16893         // so we just set the id rather than calling the setter.
16894         this.handleElId = id;
16895
16896         // the linked element is the element that gets dragged by default
16897         this.setDragElId(id);
16898
16899         // by default, clicked anchors will not start drag operations.
16900         this.invalidHandleTypes = { A: "A" };
16901         this.invalidHandleIds = {};
16902         this.invalidHandleClasses = [];
16903
16904         this.applyConfig();
16905
16906         this.handleOnAvailable();
16907     },
16908
16909     /**
16910      * Applies the configuration parameters that were passed into the constructor.
16911      * This is supposed to happen at each level through the inheritance chain.  So
16912      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16913      * DragDrop in order to get all of the parameters that are available in
16914      * each object.
16915      * @method applyConfig
16916      */
16917     applyConfig: function() {
16918
16919         // configurable properties:
16920         //    padding, isTarget, maintainOffset, primaryButtonOnly
16921         this.padding           = this.config.padding || [0, 0, 0, 0];
16922         this.isTarget          = (this.config.isTarget !== false);
16923         this.maintainOffset    = (this.config.maintainOffset);
16924         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16925
16926     },
16927
16928     /**
16929      * Executed when the linked element is available
16930      * @method handleOnAvailable
16931      * @private
16932      */
16933     handleOnAvailable: function() {
16934         this.available = true;
16935         this.resetConstraints();
16936         this.onAvailable();
16937     },
16938
16939      /**
16940      * Configures the padding for the target zone in px.  Effectively expands
16941      * (or reduces) the virtual object size for targeting calculations.
16942      * Supports css-style shorthand; if only one parameter is passed, all sides
16943      * will have that padding, and if only two are passed, the top and bottom
16944      * will have the first param, the left and right the second.
16945      * @method setPadding
16946      * @param {int} iTop    Top pad
16947      * @param {int} iRight  Right pad
16948      * @param {int} iBot    Bot pad
16949      * @param {int} iLeft   Left pad
16950      */
16951     setPadding: function(iTop, iRight, iBot, iLeft) {
16952         // this.padding = [iLeft, iRight, iTop, iBot];
16953         if (!iRight && 0 !== iRight) {
16954             this.padding = [iTop, iTop, iTop, iTop];
16955         } else if (!iBot && 0 !== iBot) {
16956             this.padding = [iTop, iRight, iTop, iRight];
16957         } else {
16958             this.padding = [iTop, iRight, iBot, iLeft];
16959         }
16960     },
16961
16962     /**
16963      * Stores the initial placement of the linked element.
16964      * @method setInitialPosition
16965      * @param {int} diffX   the X offset, default 0
16966      * @param {int} diffY   the Y offset, default 0
16967      */
16968     setInitPosition: function(diffX, diffY) {
16969         var el = this.getEl();
16970
16971         if (!this.DDM.verifyEl(el)) {
16972             return;
16973         }
16974
16975         var dx = diffX || 0;
16976         var dy = diffY || 0;
16977
16978         var p = Dom.getXY( el );
16979
16980         this.initPageX = p[0] - dx;
16981         this.initPageY = p[1] - dy;
16982
16983         this.lastPageX = p[0];
16984         this.lastPageY = p[1];
16985
16986
16987         this.setStartPosition(p);
16988     },
16989
16990     /**
16991      * Sets the start position of the element.  This is set when the obj
16992      * is initialized, the reset when a drag is started.
16993      * @method setStartPosition
16994      * @param pos current position (from previous lookup)
16995      * @private
16996      */
16997     setStartPosition: function(pos) {
16998         var p = pos || Dom.getXY( this.getEl() );
16999         this.deltaSetXY = null;
17000
17001         this.startPageX = p[0];
17002         this.startPageY = p[1];
17003     },
17004
17005     /**
17006      * Add this instance to a group of related drag/drop objects.  All
17007      * instances belong to at least one group, and can belong to as many
17008      * groups as needed.
17009      * @method addToGroup
17010      * @param sGroup {string} the name of the group
17011      */
17012     addToGroup: function(sGroup) {
17013         this.groups[sGroup] = true;
17014         this.DDM.regDragDrop(this, sGroup);
17015     },
17016
17017     /**
17018      * Remove's this instance from the supplied interaction group
17019      * @method removeFromGroup
17020      * @param {string}  sGroup  The group to drop
17021      */
17022     removeFromGroup: function(sGroup) {
17023         if (this.groups[sGroup]) {
17024             delete this.groups[sGroup];
17025         }
17026
17027         this.DDM.removeDDFromGroup(this, sGroup);
17028     },
17029
17030     /**
17031      * Allows you to specify that an element other than the linked element
17032      * will be moved with the cursor during a drag
17033      * @method setDragElId
17034      * @param id {string} the id of the element that will be used to initiate the drag
17035      */
17036     setDragElId: function(id) {
17037         this.dragElId = id;
17038     },
17039
17040     /**
17041      * Allows you to specify a child of the linked element that should be
17042      * used to initiate the drag operation.  An example of this would be if
17043      * you have a content div with text and links.  Clicking anywhere in the
17044      * content area would normally start the drag operation.  Use this method
17045      * to specify that an element inside of the content div is the element
17046      * that starts the drag operation.
17047      * @method setHandleElId
17048      * @param id {string} the id of the element that will be used to
17049      * initiate the drag.
17050      */
17051     setHandleElId: function(id) {
17052         if (typeof id !== "string") {
17053             id = Roo.id(id);
17054         }
17055         this.handleElId = id;
17056         this.DDM.regHandle(this.id, id);
17057     },
17058
17059     /**
17060      * Allows you to set an element outside of the linked element as a drag
17061      * handle
17062      * @method setOuterHandleElId
17063      * @param id the id of the element that will be used to initiate the drag
17064      */
17065     setOuterHandleElId: function(id) {
17066         if (typeof id !== "string") {
17067             id = Roo.id(id);
17068         }
17069         Event.on(id, "mousedown",
17070                 this.handleMouseDown, this);
17071         this.setHandleElId(id);
17072
17073         this.hasOuterHandles = true;
17074     },
17075
17076     /**
17077      * Remove all drag and drop hooks for this element
17078      * @method unreg
17079      */
17080     unreg: function() {
17081         Event.un(this.id, "mousedown",
17082                 this.handleMouseDown);
17083         Event.un(this.id, "touchstart",
17084                 this.handleMouseDown);
17085         this._domRef = null;
17086         this.DDM._remove(this);
17087     },
17088
17089     destroy : function(){
17090         this.unreg();
17091     },
17092
17093     /**
17094      * Returns true if this instance is locked, or the drag drop mgr is locked
17095      * (meaning that all drag/drop is disabled on the page.)
17096      * @method isLocked
17097      * @return {boolean} true if this obj or all drag/drop is locked, else
17098      * false
17099      */
17100     isLocked: function() {
17101         return (this.DDM.isLocked() || this.locked);
17102     },
17103
17104     /**
17105      * Fired when this object is clicked
17106      * @method handleMouseDown
17107      * @param {Event} e
17108      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17109      * @private
17110      */
17111     handleMouseDown: function(e, oDD){
17112      
17113         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17114             //Roo.log('not touch/ button !=0');
17115             return;
17116         }
17117         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17118             return; // double touch..
17119         }
17120         
17121
17122         if (this.isLocked()) {
17123             //Roo.log('locked');
17124             return;
17125         }
17126
17127         this.DDM.refreshCache(this.groups);
17128 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17129         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17130         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17131             //Roo.log('no outer handes or not over target');
17132                 // do nothing.
17133         } else {
17134 //            Roo.log('check validator');
17135             if (this.clickValidator(e)) {
17136 //                Roo.log('validate success');
17137                 // set the initial element position
17138                 this.setStartPosition();
17139
17140
17141                 this.b4MouseDown(e);
17142                 this.onMouseDown(e);
17143
17144                 this.DDM.handleMouseDown(e, this);
17145
17146                 this.DDM.stopEvent(e);
17147             } else {
17148
17149
17150             }
17151         }
17152     },
17153
17154     clickValidator: function(e) {
17155         var target = e.getTarget();
17156         return ( this.isValidHandleChild(target) &&
17157                     (this.id == this.handleElId ||
17158                         this.DDM.handleWasClicked(target, this.id)) );
17159     },
17160
17161     /**
17162      * Allows you to specify a tag name that should not start a drag operation
17163      * when clicked.  This is designed to facilitate embedding links within a
17164      * drag handle that do something other than start the drag.
17165      * @method addInvalidHandleType
17166      * @param {string} tagName the type of element to exclude
17167      */
17168     addInvalidHandleType: function(tagName) {
17169         var type = tagName.toUpperCase();
17170         this.invalidHandleTypes[type] = type;
17171     },
17172
17173     /**
17174      * Lets you to specify an element id for a child of a drag handle
17175      * that should not initiate a drag
17176      * @method addInvalidHandleId
17177      * @param {string} id the element id of the element you wish to ignore
17178      */
17179     addInvalidHandleId: function(id) {
17180         if (typeof id !== "string") {
17181             id = Roo.id(id);
17182         }
17183         this.invalidHandleIds[id] = id;
17184     },
17185
17186     /**
17187      * Lets you specify a css class of elements that will not initiate a drag
17188      * @method addInvalidHandleClass
17189      * @param {string} cssClass the class of the elements you wish to ignore
17190      */
17191     addInvalidHandleClass: function(cssClass) {
17192         this.invalidHandleClasses.push(cssClass);
17193     },
17194
17195     /**
17196      * Unsets an excluded tag name set by addInvalidHandleType
17197      * @method removeInvalidHandleType
17198      * @param {string} tagName the type of element to unexclude
17199      */
17200     removeInvalidHandleType: function(tagName) {
17201         var type = tagName.toUpperCase();
17202         // this.invalidHandleTypes[type] = null;
17203         delete this.invalidHandleTypes[type];
17204     },
17205
17206     /**
17207      * Unsets an invalid handle id
17208      * @method removeInvalidHandleId
17209      * @param {string} id the id of the element to re-enable
17210      */
17211     removeInvalidHandleId: function(id) {
17212         if (typeof id !== "string") {
17213             id = Roo.id(id);
17214         }
17215         delete this.invalidHandleIds[id];
17216     },
17217
17218     /**
17219      * Unsets an invalid css class
17220      * @method removeInvalidHandleClass
17221      * @param {string} cssClass the class of the element(s) you wish to
17222      * re-enable
17223      */
17224     removeInvalidHandleClass: function(cssClass) {
17225         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17226             if (this.invalidHandleClasses[i] == cssClass) {
17227                 delete this.invalidHandleClasses[i];
17228             }
17229         }
17230     },
17231
17232     /**
17233      * Checks the tag exclusion list to see if this click should be ignored
17234      * @method isValidHandleChild
17235      * @param {HTMLElement} node the HTMLElement to evaluate
17236      * @return {boolean} true if this is a valid tag type, false if not
17237      */
17238     isValidHandleChild: function(node) {
17239
17240         var valid = true;
17241         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17242         var nodeName;
17243         try {
17244             nodeName = node.nodeName.toUpperCase();
17245         } catch(e) {
17246             nodeName = node.nodeName;
17247         }
17248         valid = valid && !this.invalidHandleTypes[nodeName];
17249         valid = valid && !this.invalidHandleIds[node.id];
17250
17251         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17252             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17253         }
17254
17255
17256         return valid;
17257
17258     },
17259
17260     /**
17261      * Create the array of horizontal tick marks if an interval was specified
17262      * in setXConstraint().
17263      * @method setXTicks
17264      * @private
17265      */
17266     setXTicks: function(iStartX, iTickSize) {
17267         this.xTicks = [];
17268         this.xTickSize = iTickSize;
17269
17270         var tickMap = {};
17271
17272         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17273             if (!tickMap[i]) {
17274                 this.xTicks[this.xTicks.length] = i;
17275                 tickMap[i] = true;
17276             }
17277         }
17278
17279         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17280             if (!tickMap[i]) {
17281                 this.xTicks[this.xTicks.length] = i;
17282                 tickMap[i] = true;
17283             }
17284         }
17285
17286         this.xTicks.sort(this.DDM.numericSort) ;
17287     },
17288
17289     /**
17290      * Create the array of vertical tick marks if an interval was specified in
17291      * setYConstraint().
17292      * @method setYTicks
17293      * @private
17294      */
17295     setYTicks: function(iStartY, iTickSize) {
17296         this.yTicks = [];
17297         this.yTickSize = iTickSize;
17298
17299         var tickMap = {};
17300
17301         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17302             if (!tickMap[i]) {
17303                 this.yTicks[this.yTicks.length] = i;
17304                 tickMap[i] = true;
17305             }
17306         }
17307
17308         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17309             if (!tickMap[i]) {
17310                 this.yTicks[this.yTicks.length] = i;
17311                 tickMap[i] = true;
17312             }
17313         }
17314
17315         this.yTicks.sort(this.DDM.numericSort) ;
17316     },
17317
17318     /**
17319      * By default, the element can be dragged any place on the screen.  Use
17320      * this method to limit the horizontal travel of the element.  Pass in
17321      * 0,0 for the parameters if you want to lock the drag to the y axis.
17322      * @method setXConstraint
17323      * @param {int} iLeft the number of pixels the element can move to the left
17324      * @param {int} iRight the number of pixels the element can move to the
17325      * right
17326      * @param {int} iTickSize optional parameter for specifying that the
17327      * element
17328      * should move iTickSize pixels at a time.
17329      */
17330     setXConstraint: function(iLeft, iRight, iTickSize) {
17331         this.leftConstraint = iLeft;
17332         this.rightConstraint = iRight;
17333
17334         this.minX = this.initPageX - iLeft;
17335         this.maxX = this.initPageX + iRight;
17336         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17337
17338         this.constrainX = true;
17339     },
17340
17341     /**
17342      * Clears any constraints applied to this instance.  Also clears ticks
17343      * since they can't exist independent of a constraint at this time.
17344      * @method clearConstraints
17345      */
17346     clearConstraints: function() {
17347         this.constrainX = false;
17348         this.constrainY = false;
17349         this.clearTicks();
17350     },
17351
17352     /**
17353      * Clears any tick interval defined for this instance
17354      * @method clearTicks
17355      */
17356     clearTicks: function() {
17357         this.xTicks = null;
17358         this.yTicks = null;
17359         this.xTickSize = 0;
17360         this.yTickSize = 0;
17361     },
17362
17363     /**
17364      * By default, the element can be dragged any place on the screen.  Set
17365      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17366      * parameters if you want to lock the drag to the x axis.
17367      * @method setYConstraint
17368      * @param {int} iUp the number of pixels the element can move up
17369      * @param {int} iDown the number of pixels the element can move down
17370      * @param {int} iTickSize optional parameter for specifying that the
17371      * element should move iTickSize pixels at a time.
17372      */
17373     setYConstraint: function(iUp, iDown, iTickSize) {
17374         this.topConstraint = iUp;
17375         this.bottomConstraint = iDown;
17376
17377         this.minY = this.initPageY - iUp;
17378         this.maxY = this.initPageY + iDown;
17379         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17380
17381         this.constrainY = true;
17382
17383     },
17384
17385     /**
17386      * resetConstraints must be called if you manually reposition a dd element.
17387      * @method resetConstraints
17388      * @param {boolean} maintainOffset
17389      */
17390     resetConstraints: function() {
17391
17392
17393         // Maintain offsets if necessary
17394         if (this.initPageX || this.initPageX === 0) {
17395             // figure out how much this thing has moved
17396             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17397             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17398
17399             this.setInitPosition(dx, dy);
17400
17401         // This is the first time we have detected the element's position
17402         } else {
17403             this.setInitPosition();
17404         }
17405
17406         if (this.constrainX) {
17407             this.setXConstraint( this.leftConstraint,
17408                                  this.rightConstraint,
17409                                  this.xTickSize        );
17410         }
17411
17412         if (this.constrainY) {
17413             this.setYConstraint( this.topConstraint,
17414                                  this.bottomConstraint,
17415                                  this.yTickSize         );
17416         }
17417     },
17418
17419     /**
17420      * Normally the drag element is moved pixel by pixel, but we can specify
17421      * that it move a number of pixels at a time.  This method resolves the
17422      * location when we have it set up like this.
17423      * @method getTick
17424      * @param {int} val where we want to place the object
17425      * @param {int[]} tickArray sorted array of valid points
17426      * @return {int} the closest tick
17427      * @private
17428      */
17429     getTick: function(val, tickArray) {
17430
17431         if (!tickArray) {
17432             // If tick interval is not defined, it is effectively 1 pixel,
17433             // so we return the value passed to us.
17434             return val;
17435         } else if (tickArray[0] >= val) {
17436             // The value is lower than the first tick, so we return the first
17437             // tick.
17438             return tickArray[0];
17439         } else {
17440             for (var i=0, len=tickArray.length; i<len; ++i) {
17441                 var next = i + 1;
17442                 if (tickArray[next] && tickArray[next] >= val) {
17443                     var diff1 = val - tickArray[i];
17444                     var diff2 = tickArray[next] - val;
17445                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17446                 }
17447             }
17448
17449             // The value is larger than the last tick, so we return the last
17450             // tick.
17451             return tickArray[tickArray.length - 1];
17452         }
17453     },
17454
17455     /**
17456      * toString method
17457      * @method toString
17458      * @return {string} string representation of the dd obj
17459      */
17460     toString: function() {
17461         return ("DragDrop " + this.id);
17462     }
17463
17464 });
17465
17466 })();
17467 /*
17468  * Based on:
17469  * Ext JS Library 1.1.1
17470  * Copyright(c) 2006-2007, Ext JS, LLC.
17471  *
17472  * Originally Released Under LGPL - original licence link has changed is not relivant.
17473  *
17474  * Fork - LGPL
17475  * <script type="text/javascript">
17476  */
17477
17478
17479 /**
17480  * The drag and drop utility provides a framework for building drag and drop
17481  * applications.  In addition to enabling drag and drop for specific elements,
17482  * the drag and drop elements are tracked by the manager class, and the
17483  * interactions between the various elements are tracked during the drag and
17484  * the implementing code is notified about these important moments.
17485  */
17486
17487 // Only load the library once.  Rewriting the manager class would orphan
17488 // existing drag and drop instances.
17489 if (!Roo.dd.DragDropMgr) {
17490
17491 /**
17492  * @class Roo.dd.DragDropMgr
17493  * DragDropMgr is a singleton that tracks the element interaction for
17494  * all DragDrop items in the window.  Generally, you will not call
17495  * this class directly, but it does have helper methods that could
17496  * be useful in your DragDrop implementations.
17497  * @singleton
17498  */
17499 Roo.dd.DragDropMgr = function() {
17500
17501     var Event = Roo.EventManager;
17502
17503     return {
17504
17505         /**
17506          * Two dimensional Array of registered DragDrop objects.  The first
17507          * dimension is the DragDrop item group, the second the DragDrop
17508          * object.
17509          * @property ids
17510          * @type {string: string}
17511          * @private
17512          * @static
17513          */
17514         ids: {},
17515
17516         /**
17517          * Array of element ids defined as drag handles.  Used to determine
17518          * if the element that generated the mousedown event is actually the
17519          * handle and not the html element itself.
17520          * @property handleIds
17521          * @type {string: string}
17522          * @private
17523          * @static
17524          */
17525         handleIds: {},
17526
17527         /**
17528          * the DragDrop object that is currently being dragged
17529          * @property dragCurrent
17530          * @type DragDrop
17531          * @private
17532          * @static
17533          **/
17534         dragCurrent: null,
17535
17536         /**
17537          * the DragDrop object(s) that are being hovered over
17538          * @property dragOvers
17539          * @type Array
17540          * @private
17541          * @static
17542          */
17543         dragOvers: {},
17544
17545         /**
17546          * the X distance between the cursor and the object being dragged
17547          * @property deltaX
17548          * @type int
17549          * @private
17550          * @static
17551          */
17552         deltaX: 0,
17553
17554         /**
17555          * the Y distance between the cursor and the object being dragged
17556          * @property deltaY
17557          * @type int
17558          * @private
17559          * @static
17560          */
17561         deltaY: 0,
17562
17563         /**
17564          * Flag to determine if we should prevent the default behavior of the
17565          * events we define. By default this is true, but this can be set to
17566          * false if you need the default behavior (not recommended)
17567          * @property preventDefault
17568          * @type boolean
17569          * @static
17570          */
17571         preventDefault: true,
17572
17573         /**
17574          * Flag to determine if we should stop the propagation of the events
17575          * we generate. This is true by default but you may want to set it to
17576          * false if the html element contains other features that require the
17577          * mouse click.
17578          * @property stopPropagation
17579          * @type boolean
17580          * @static
17581          */
17582         stopPropagation: true,
17583
17584         /**
17585          * Internal flag that is set to true when drag and drop has been
17586          * intialized
17587          * @property initialized
17588          * @private
17589          * @static
17590          */
17591         initalized: false,
17592
17593         /**
17594          * All drag and drop can be disabled.
17595          * @property locked
17596          * @private
17597          * @static
17598          */
17599         locked: false,
17600
17601         /**
17602          * Called the first time an element is registered.
17603          * @method init
17604          * @private
17605          * @static
17606          */
17607         init: function() {
17608             this.initialized = true;
17609         },
17610
17611         /**
17612          * In point mode, drag and drop interaction is defined by the
17613          * location of the cursor during the drag/drop
17614          * @property POINT
17615          * @type int
17616          * @static
17617          */
17618         POINT: 0,
17619
17620         /**
17621          * In intersect mode, drag and drop interactio nis defined by the
17622          * overlap of two or more drag and drop objects.
17623          * @property INTERSECT
17624          * @type int
17625          * @static
17626          */
17627         INTERSECT: 1,
17628
17629         /**
17630          * The current drag and drop mode.  Default: POINT
17631          * @property mode
17632          * @type int
17633          * @static
17634          */
17635         mode: 0,
17636
17637         /**
17638          * Runs method on all drag and drop objects
17639          * @method _execOnAll
17640          * @private
17641          * @static
17642          */
17643         _execOnAll: function(sMethod, args) {
17644             for (var i in this.ids) {
17645                 for (var j in this.ids[i]) {
17646                     var oDD = this.ids[i][j];
17647                     if (! this.isTypeOfDD(oDD)) {
17648                         continue;
17649                     }
17650                     oDD[sMethod].apply(oDD, args);
17651                 }
17652             }
17653         },
17654
17655         /**
17656          * Drag and drop initialization.  Sets up the global event handlers
17657          * @method _onLoad
17658          * @private
17659          * @static
17660          */
17661         _onLoad: function() {
17662
17663             this.init();
17664
17665             if (!Roo.isTouch) {
17666                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17667                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17668             }
17669             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17670             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17671             
17672             Event.on(window,   "unload",    this._onUnload, this, true);
17673             Event.on(window,   "resize",    this._onResize, this, true);
17674             // Event.on(window,   "mouseout",    this._test);
17675
17676         },
17677
17678         /**
17679          * Reset constraints on all drag and drop objs
17680          * @method _onResize
17681          * @private
17682          * @static
17683          */
17684         _onResize: function(e) {
17685             this._execOnAll("resetConstraints", []);
17686         },
17687
17688         /**
17689          * Lock all drag and drop functionality
17690          * @method lock
17691          * @static
17692          */
17693         lock: function() { this.locked = true; },
17694
17695         /**
17696          * Unlock all drag and drop functionality
17697          * @method unlock
17698          * @static
17699          */
17700         unlock: function() { this.locked = false; },
17701
17702         /**
17703          * Is drag and drop locked?
17704          * @method isLocked
17705          * @return {boolean} True if drag and drop is locked, false otherwise.
17706          * @static
17707          */
17708         isLocked: function() { return this.locked; },
17709
17710         /**
17711          * Location cache that is set for all drag drop objects when a drag is
17712          * initiated, cleared when the drag is finished.
17713          * @property locationCache
17714          * @private
17715          * @static
17716          */
17717         locationCache: {},
17718
17719         /**
17720          * Set useCache to false if you want to force object the lookup of each
17721          * drag and drop linked element constantly during a drag.
17722          * @property useCache
17723          * @type boolean
17724          * @static
17725          */
17726         useCache: true,
17727
17728         /**
17729          * The number of pixels that the mouse needs to move after the
17730          * mousedown before the drag is initiated.  Default=3;
17731          * @property clickPixelThresh
17732          * @type int
17733          * @static
17734          */
17735         clickPixelThresh: 3,
17736
17737         /**
17738          * The number of milliseconds after the mousedown event to initiate the
17739          * drag if we don't get a mouseup event. Default=1000
17740          * @property clickTimeThresh
17741          * @type int
17742          * @static
17743          */
17744         clickTimeThresh: 350,
17745
17746         /**
17747          * Flag that indicates that either the drag pixel threshold or the
17748          * mousdown time threshold has been met
17749          * @property dragThreshMet
17750          * @type boolean
17751          * @private
17752          * @static
17753          */
17754         dragThreshMet: false,
17755
17756         /**
17757          * Timeout used for the click time threshold
17758          * @property clickTimeout
17759          * @type Object
17760          * @private
17761          * @static
17762          */
17763         clickTimeout: null,
17764
17765         /**
17766          * The X position of the mousedown event stored for later use when a
17767          * drag threshold is met.
17768          * @property startX
17769          * @type int
17770          * @private
17771          * @static
17772          */
17773         startX: 0,
17774
17775         /**
17776          * The Y position of the mousedown event stored for later use when a
17777          * drag threshold is met.
17778          * @property startY
17779          * @type int
17780          * @private
17781          * @static
17782          */
17783         startY: 0,
17784
17785         /**
17786          * Each DragDrop instance must be registered with the DragDropMgr.
17787          * This is executed in DragDrop.init()
17788          * @method regDragDrop
17789          * @param {DragDrop} oDD the DragDrop object to register
17790          * @param {String} sGroup the name of the group this element belongs to
17791          * @static
17792          */
17793         regDragDrop: function(oDD, sGroup) {
17794             if (!this.initialized) { this.init(); }
17795
17796             if (!this.ids[sGroup]) {
17797                 this.ids[sGroup] = {};
17798             }
17799             this.ids[sGroup][oDD.id] = oDD;
17800         },
17801
17802         /**
17803          * Removes the supplied dd instance from the supplied group. Executed
17804          * by DragDrop.removeFromGroup, so don't call this function directly.
17805          * @method removeDDFromGroup
17806          * @private
17807          * @static
17808          */
17809         removeDDFromGroup: function(oDD, sGroup) {
17810             if (!this.ids[sGroup]) {
17811                 this.ids[sGroup] = {};
17812             }
17813
17814             var obj = this.ids[sGroup];
17815             if (obj && obj[oDD.id]) {
17816                 delete obj[oDD.id];
17817             }
17818         },
17819
17820         /**
17821          * Unregisters a drag and drop item.  This is executed in
17822          * DragDrop.unreg, use that method instead of calling this directly.
17823          * @method _remove
17824          * @private
17825          * @static
17826          */
17827         _remove: function(oDD) {
17828             for (var g in oDD.groups) {
17829                 if (g && this.ids[g][oDD.id]) {
17830                     delete this.ids[g][oDD.id];
17831                 }
17832             }
17833             delete this.handleIds[oDD.id];
17834         },
17835
17836         /**
17837          * Each DragDrop handle element must be registered.  This is done
17838          * automatically when executing DragDrop.setHandleElId()
17839          * @method regHandle
17840          * @param {String} sDDId the DragDrop id this element is a handle for
17841          * @param {String} sHandleId the id of the element that is the drag
17842          * handle
17843          * @static
17844          */
17845         regHandle: function(sDDId, sHandleId) {
17846             if (!this.handleIds[sDDId]) {
17847                 this.handleIds[sDDId] = {};
17848             }
17849             this.handleIds[sDDId][sHandleId] = sHandleId;
17850         },
17851
17852         /**
17853          * Utility function to determine if a given element has been
17854          * registered as a drag drop item.
17855          * @method isDragDrop
17856          * @param {String} id the element id to check
17857          * @return {boolean} true if this element is a DragDrop item,
17858          * false otherwise
17859          * @static
17860          */
17861         isDragDrop: function(id) {
17862             return ( this.getDDById(id) ) ? true : false;
17863         },
17864
17865         /**
17866          * Returns the drag and drop instances that are in all groups the
17867          * passed in instance belongs to.
17868          * @method getRelated
17869          * @param {DragDrop} p_oDD the obj to get related data for
17870          * @param {boolean} bTargetsOnly if true, only return targetable objs
17871          * @return {DragDrop[]} the related instances
17872          * @static
17873          */
17874         getRelated: function(p_oDD, bTargetsOnly) {
17875             var oDDs = [];
17876             for (var i in p_oDD.groups) {
17877                 for (j in this.ids[i]) {
17878                     var dd = this.ids[i][j];
17879                     if (! this.isTypeOfDD(dd)) {
17880                         continue;
17881                     }
17882                     if (!bTargetsOnly || dd.isTarget) {
17883                         oDDs[oDDs.length] = dd;
17884                     }
17885                 }
17886             }
17887
17888             return oDDs;
17889         },
17890
17891         /**
17892          * Returns true if the specified dd target is a legal target for
17893          * the specifice drag obj
17894          * @method isLegalTarget
17895          * @param {DragDrop} the drag obj
17896          * @param {DragDrop} the target
17897          * @return {boolean} true if the target is a legal target for the
17898          * dd obj
17899          * @static
17900          */
17901         isLegalTarget: function (oDD, oTargetDD) {
17902             var targets = this.getRelated(oDD, true);
17903             for (var i=0, len=targets.length;i<len;++i) {
17904                 if (targets[i].id == oTargetDD.id) {
17905                     return true;
17906                 }
17907             }
17908
17909             return false;
17910         },
17911
17912         /**
17913          * My goal is to be able to transparently determine if an object is
17914          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17915          * returns "object", oDD.constructor.toString() always returns
17916          * "DragDrop" and not the name of the subclass.  So for now it just
17917          * evaluates a well-known variable in DragDrop.
17918          * @method isTypeOfDD
17919          * @param {Object} the object to evaluate
17920          * @return {boolean} true if typeof oDD = DragDrop
17921          * @static
17922          */
17923         isTypeOfDD: function (oDD) {
17924             return (oDD && oDD.__ygDragDrop);
17925         },
17926
17927         /**
17928          * Utility function to determine if a given element has been
17929          * registered as a drag drop handle for the given Drag Drop object.
17930          * @method isHandle
17931          * @param {String} id the element id to check
17932          * @return {boolean} true if this element is a DragDrop handle, false
17933          * otherwise
17934          * @static
17935          */
17936         isHandle: function(sDDId, sHandleId) {
17937             return ( this.handleIds[sDDId] &&
17938                             this.handleIds[sDDId][sHandleId] );
17939         },
17940
17941         /**
17942          * Returns the DragDrop instance for a given id
17943          * @method getDDById
17944          * @param {String} id the id of the DragDrop object
17945          * @return {DragDrop} the drag drop object, null if it is not found
17946          * @static
17947          */
17948         getDDById: function(id) {
17949             for (var i in this.ids) {
17950                 if (this.ids[i][id]) {
17951                     return this.ids[i][id];
17952                 }
17953             }
17954             return null;
17955         },
17956
17957         /**
17958          * Fired after a registered DragDrop object gets the mousedown event.
17959          * Sets up the events required to track the object being dragged
17960          * @method handleMouseDown
17961          * @param {Event} e the event
17962          * @param oDD the DragDrop object being dragged
17963          * @private
17964          * @static
17965          */
17966         handleMouseDown: function(e, oDD) {
17967             if(Roo.QuickTips){
17968                 Roo.QuickTips.disable();
17969             }
17970             this.currentTarget = e.getTarget();
17971
17972             this.dragCurrent = oDD;
17973
17974             var el = oDD.getEl();
17975
17976             // track start position
17977             this.startX = e.getPageX();
17978             this.startY = e.getPageY();
17979
17980             this.deltaX = this.startX - el.offsetLeft;
17981             this.deltaY = this.startY - el.offsetTop;
17982
17983             this.dragThreshMet = false;
17984
17985             this.clickTimeout = setTimeout(
17986                     function() {
17987                         var DDM = Roo.dd.DDM;
17988                         DDM.startDrag(DDM.startX, DDM.startY);
17989                     },
17990                     this.clickTimeThresh );
17991         },
17992
17993         /**
17994          * Fired when either the drag pixel threshol or the mousedown hold
17995          * time threshold has been met.
17996          * @method startDrag
17997          * @param x {int} the X position of the original mousedown
17998          * @param y {int} the Y position of the original mousedown
17999          * @static
18000          */
18001         startDrag: function(x, y) {
18002             clearTimeout(this.clickTimeout);
18003             if (this.dragCurrent) {
18004                 this.dragCurrent.b4StartDrag(x, y);
18005                 this.dragCurrent.startDrag(x, y);
18006             }
18007             this.dragThreshMet = true;
18008         },
18009
18010         /**
18011          * Internal function to handle the mouseup event.  Will be invoked
18012          * from the context of the document.
18013          * @method handleMouseUp
18014          * @param {Event} e the event
18015          * @private
18016          * @static
18017          */
18018         handleMouseUp: function(e) {
18019
18020             if(Roo.QuickTips){
18021                 Roo.QuickTips.enable();
18022             }
18023             if (! this.dragCurrent) {
18024                 return;
18025             }
18026
18027             clearTimeout(this.clickTimeout);
18028
18029             if (this.dragThreshMet) {
18030                 this.fireEvents(e, true);
18031             } else {
18032             }
18033
18034             this.stopDrag(e);
18035
18036             this.stopEvent(e);
18037         },
18038
18039         /**
18040          * Utility to stop event propagation and event default, if these
18041          * features are turned on.
18042          * @method stopEvent
18043          * @param {Event} e the event as returned by this.getEvent()
18044          * @static
18045          */
18046         stopEvent: function(e){
18047             if(this.stopPropagation) {
18048                 e.stopPropagation();
18049             }
18050
18051             if (this.preventDefault) {
18052                 e.preventDefault();
18053             }
18054         },
18055
18056         /**
18057          * Internal function to clean up event handlers after the drag
18058          * operation is complete
18059          * @method stopDrag
18060          * @param {Event} e the event
18061          * @private
18062          * @static
18063          */
18064         stopDrag: function(e) {
18065             // Fire the drag end event for the item that was dragged
18066             if (this.dragCurrent) {
18067                 if (this.dragThreshMet) {
18068                     this.dragCurrent.b4EndDrag(e);
18069                     this.dragCurrent.endDrag(e);
18070                 }
18071
18072                 this.dragCurrent.onMouseUp(e);
18073             }
18074
18075             this.dragCurrent = null;
18076             this.dragOvers = {};
18077         },
18078
18079         /**
18080          * Internal function to handle the mousemove event.  Will be invoked
18081          * from the context of the html element.
18082          *
18083          * @TODO figure out what we can do about mouse events lost when the
18084          * user drags objects beyond the window boundary.  Currently we can
18085          * detect this in internet explorer by verifying that the mouse is
18086          * down during the mousemove event.  Firefox doesn't give us the
18087          * button state on the mousemove event.
18088          * @method handleMouseMove
18089          * @param {Event} e the event
18090          * @private
18091          * @static
18092          */
18093         handleMouseMove: function(e) {
18094             if (! this.dragCurrent) {
18095                 return true;
18096             }
18097
18098             // var button = e.which || e.button;
18099
18100             // check for IE mouseup outside of page boundary
18101             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18102                 this.stopEvent(e);
18103                 return this.handleMouseUp(e);
18104             }
18105
18106             if (!this.dragThreshMet) {
18107                 var diffX = Math.abs(this.startX - e.getPageX());
18108                 var diffY = Math.abs(this.startY - e.getPageY());
18109                 if (diffX > this.clickPixelThresh ||
18110                             diffY > this.clickPixelThresh) {
18111                     this.startDrag(this.startX, this.startY);
18112                 }
18113             }
18114
18115             if (this.dragThreshMet) {
18116                 this.dragCurrent.b4Drag(e);
18117                 this.dragCurrent.onDrag(e);
18118                 if(!this.dragCurrent.moveOnly){
18119                     this.fireEvents(e, false);
18120                 }
18121             }
18122
18123             this.stopEvent(e);
18124
18125             return true;
18126         },
18127
18128         /**
18129          * Iterates over all of the DragDrop elements to find ones we are
18130          * hovering over or dropping on
18131          * @method fireEvents
18132          * @param {Event} e the event
18133          * @param {boolean} isDrop is this a drop op or a mouseover op?
18134          * @private
18135          * @static
18136          */
18137         fireEvents: function(e, isDrop) {
18138             var dc = this.dragCurrent;
18139
18140             // If the user did the mouse up outside of the window, we could
18141             // get here even though we have ended the drag.
18142             if (!dc || dc.isLocked()) {
18143                 return;
18144             }
18145
18146             var pt = e.getPoint();
18147
18148             // cache the previous dragOver array
18149             var oldOvers = [];
18150
18151             var outEvts   = [];
18152             var overEvts  = [];
18153             var dropEvts  = [];
18154             var enterEvts = [];
18155
18156             // Check to see if the object(s) we were hovering over is no longer
18157             // being hovered over so we can fire the onDragOut event
18158             for (var i in this.dragOvers) {
18159
18160                 var ddo = this.dragOvers[i];
18161
18162                 if (! this.isTypeOfDD(ddo)) {
18163                     continue;
18164                 }
18165
18166                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18167                     outEvts.push( ddo );
18168                 }
18169
18170                 oldOvers[i] = true;
18171                 delete this.dragOvers[i];
18172             }
18173
18174             for (var sGroup in dc.groups) {
18175
18176                 if ("string" != typeof sGroup) {
18177                     continue;
18178                 }
18179
18180                 for (i in this.ids[sGroup]) {
18181                     var oDD = this.ids[sGroup][i];
18182                     if (! this.isTypeOfDD(oDD)) {
18183                         continue;
18184                     }
18185
18186                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18187                         if (this.isOverTarget(pt, oDD, this.mode)) {
18188                             // look for drop interactions
18189                             if (isDrop) {
18190                                 dropEvts.push( oDD );
18191                             // look for drag enter and drag over interactions
18192                             } else {
18193
18194                                 // initial drag over: dragEnter fires
18195                                 if (!oldOvers[oDD.id]) {
18196                                     enterEvts.push( oDD );
18197                                 // subsequent drag overs: dragOver fires
18198                                 } else {
18199                                     overEvts.push( oDD );
18200                                 }
18201
18202                                 this.dragOvers[oDD.id] = oDD;
18203                             }
18204                         }
18205                     }
18206                 }
18207             }
18208
18209             if (this.mode) {
18210                 if (outEvts.length) {
18211                     dc.b4DragOut(e, outEvts);
18212                     dc.onDragOut(e, outEvts);
18213                 }
18214
18215                 if (enterEvts.length) {
18216                     dc.onDragEnter(e, enterEvts);
18217                 }
18218
18219                 if (overEvts.length) {
18220                     dc.b4DragOver(e, overEvts);
18221                     dc.onDragOver(e, overEvts);
18222                 }
18223
18224                 if (dropEvts.length) {
18225                     dc.b4DragDrop(e, dropEvts);
18226                     dc.onDragDrop(e, dropEvts);
18227                 }
18228
18229             } else {
18230                 // fire dragout events
18231                 var len = 0;
18232                 for (i=0, len=outEvts.length; i<len; ++i) {
18233                     dc.b4DragOut(e, outEvts[i].id);
18234                     dc.onDragOut(e, outEvts[i].id);
18235                 }
18236
18237                 // fire enter events
18238                 for (i=0,len=enterEvts.length; i<len; ++i) {
18239                     // dc.b4DragEnter(e, oDD.id);
18240                     dc.onDragEnter(e, enterEvts[i].id);
18241                 }
18242
18243                 // fire over events
18244                 for (i=0,len=overEvts.length; i<len; ++i) {
18245                     dc.b4DragOver(e, overEvts[i].id);
18246                     dc.onDragOver(e, overEvts[i].id);
18247                 }
18248
18249                 // fire drop events
18250                 for (i=0, len=dropEvts.length; i<len; ++i) {
18251                     dc.b4DragDrop(e, dropEvts[i].id);
18252                     dc.onDragDrop(e, dropEvts[i].id);
18253                 }
18254
18255             }
18256
18257             // notify about a drop that did not find a target
18258             if (isDrop && !dropEvts.length) {
18259                 dc.onInvalidDrop(e);
18260             }
18261
18262         },
18263
18264         /**
18265          * Helper function for getting the best match from the list of drag
18266          * and drop objects returned by the drag and drop events when we are
18267          * in INTERSECT mode.  It returns either the first object that the
18268          * cursor is over, or the object that has the greatest overlap with
18269          * the dragged element.
18270          * @method getBestMatch
18271          * @param  {DragDrop[]} dds The array of drag and drop objects
18272          * targeted
18273          * @return {DragDrop}       The best single match
18274          * @static
18275          */
18276         getBestMatch: function(dds) {
18277             var winner = null;
18278             // Return null if the input is not what we expect
18279             //if (!dds || !dds.length || dds.length == 0) {
18280                // winner = null;
18281             // If there is only one item, it wins
18282             //} else if (dds.length == 1) {
18283
18284             var len = dds.length;
18285
18286             if (len == 1) {
18287                 winner = dds[0];
18288             } else {
18289                 // Loop through the targeted items
18290                 for (var i=0; i<len; ++i) {
18291                     var dd = dds[i];
18292                     // If the cursor is over the object, it wins.  If the
18293                     // cursor is over multiple matches, the first one we come
18294                     // to wins.
18295                     if (dd.cursorIsOver) {
18296                         winner = dd;
18297                         break;
18298                     // Otherwise the object with the most overlap wins
18299                     } else {
18300                         if (!winner ||
18301                             winner.overlap.getArea() < dd.overlap.getArea()) {
18302                             winner = dd;
18303                         }
18304                     }
18305                 }
18306             }
18307
18308             return winner;
18309         },
18310
18311         /**
18312          * Refreshes the cache of the top-left and bottom-right points of the
18313          * drag and drop objects in the specified group(s).  This is in the
18314          * format that is stored in the drag and drop instance, so typical
18315          * usage is:
18316          * <code>
18317          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18318          * </code>
18319          * Alternatively:
18320          * <code>
18321          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18322          * </code>
18323          * @TODO this really should be an indexed array.  Alternatively this
18324          * method could accept both.
18325          * @method refreshCache
18326          * @param {Object} groups an associative array of groups to refresh
18327          * @static
18328          */
18329         refreshCache: function(groups) {
18330             for (var sGroup in groups) {
18331                 if ("string" != typeof sGroup) {
18332                     continue;
18333                 }
18334                 for (var i in this.ids[sGroup]) {
18335                     var oDD = this.ids[sGroup][i];
18336
18337                     if (this.isTypeOfDD(oDD)) {
18338                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18339                         var loc = this.getLocation(oDD);
18340                         if (loc) {
18341                             this.locationCache[oDD.id] = loc;
18342                         } else {
18343                             delete this.locationCache[oDD.id];
18344                             // this will unregister the drag and drop object if
18345                             // the element is not in a usable state
18346                             // oDD.unreg();
18347                         }
18348                     }
18349                 }
18350             }
18351         },
18352
18353         /**
18354          * This checks to make sure an element exists and is in the DOM.  The
18355          * main purpose is to handle cases where innerHTML is used to remove
18356          * drag and drop objects from the DOM.  IE provides an 'unspecified
18357          * error' when trying to access the offsetParent of such an element
18358          * @method verifyEl
18359          * @param {HTMLElement} el the element to check
18360          * @return {boolean} true if the element looks usable
18361          * @static
18362          */
18363         verifyEl: function(el) {
18364             if (el) {
18365                 var parent;
18366                 if(Roo.isIE){
18367                     try{
18368                         parent = el.offsetParent;
18369                     }catch(e){}
18370                 }else{
18371                     parent = el.offsetParent;
18372                 }
18373                 if (parent) {
18374                     return true;
18375                 }
18376             }
18377
18378             return false;
18379         },
18380
18381         /**
18382          * Returns a Region object containing the drag and drop element's position
18383          * and size, including the padding configured for it
18384          * @method getLocation
18385          * @param {DragDrop} oDD the drag and drop object to get the
18386          *                       location for
18387          * @return {Roo.lib.Region} a Region object representing the total area
18388          *                             the element occupies, including any padding
18389          *                             the instance is configured for.
18390          * @static
18391          */
18392         getLocation: function(oDD) {
18393             if (! this.isTypeOfDD(oDD)) {
18394                 return null;
18395             }
18396
18397             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18398
18399             try {
18400                 pos= Roo.lib.Dom.getXY(el);
18401             } catch (e) { }
18402
18403             if (!pos) {
18404                 return null;
18405             }
18406
18407             x1 = pos[0];
18408             x2 = x1 + el.offsetWidth;
18409             y1 = pos[1];
18410             y2 = y1 + el.offsetHeight;
18411
18412             t = y1 - oDD.padding[0];
18413             r = x2 + oDD.padding[1];
18414             b = y2 + oDD.padding[2];
18415             l = x1 - oDD.padding[3];
18416
18417             return new Roo.lib.Region( t, r, b, l );
18418         },
18419
18420         /**
18421          * Checks the cursor location to see if it over the target
18422          * @method isOverTarget
18423          * @param {Roo.lib.Point} pt The point to evaluate
18424          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18425          * @return {boolean} true if the mouse is over the target
18426          * @private
18427          * @static
18428          */
18429         isOverTarget: function(pt, oTarget, intersect) {
18430             // use cache if available
18431             var loc = this.locationCache[oTarget.id];
18432             if (!loc || !this.useCache) {
18433                 loc = this.getLocation(oTarget);
18434                 this.locationCache[oTarget.id] = loc;
18435
18436             }
18437
18438             if (!loc) {
18439                 return false;
18440             }
18441
18442             oTarget.cursorIsOver = loc.contains( pt );
18443
18444             // DragDrop is using this as a sanity check for the initial mousedown
18445             // in this case we are done.  In POINT mode, if the drag obj has no
18446             // contraints, we are also done. Otherwise we need to evaluate the
18447             // location of the target as related to the actual location of the
18448             // dragged element.
18449             var dc = this.dragCurrent;
18450             if (!dc || !dc.getTargetCoord ||
18451                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18452                 return oTarget.cursorIsOver;
18453             }
18454
18455             oTarget.overlap = null;
18456
18457             // Get the current location of the drag element, this is the
18458             // location of the mouse event less the delta that represents
18459             // where the original mousedown happened on the element.  We
18460             // need to consider constraints and ticks as well.
18461             var pos = dc.getTargetCoord(pt.x, pt.y);
18462
18463             var el = dc.getDragEl();
18464             var curRegion = new Roo.lib.Region( pos.y,
18465                                                    pos.x + el.offsetWidth,
18466                                                    pos.y + el.offsetHeight,
18467                                                    pos.x );
18468
18469             var overlap = curRegion.intersect(loc);
18470
18471             if (overlap) {
18472                 oTarget.overlap = overlap;
18473                 return (intersect) ? true : oTarget.cursorIsOver;
18474             } else {
18475                 return false;
18476             }
18477         },
18478
18479         /**
18480          * unload event handler
18481          * @method _onUnload
18482          * @private
18483          * @static
18484          */
18485         _onUnload: function(e, me) {
18486             Roo.dd.DragDropMgr.unregAll();
18487         },
18488
18489         /**
18490          * Cleans up the drag and drop events and objects.
18491          * @method unregAll
18492          * @private
18493          * @static
18494          */
18495         unregAll: function() {
18496
18497             if (this.dragCurrent) {
18498                 this.stopDrag();
18499                 this.dragCurrent = null;
18500             }
18501
18502             this._execOnAll("unreg", []);
18503
18504             for (i in this.elementCache) {
18505                 delete this.elementCache[i];
18506             }
18507
18508             this.elementCache = {};
18509             this.ids = {};
18510         },
18511
18512         /**
18513          * A cache of DOM elements
18514          * @property elementCache
18515          * @private
18516          * @static
18517          */
18518         elementCache: {},
18519
18520         /**
18521          * Get the wrapper for the DOM element specified
18522          * @method getElWrapper
18523          * @param {String} id the id of the element to get
18524          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18525          * @private
18526          * @deprecated This wrapper isn't that useful
18527          * @static
18528          */
18529         getElWrapper: function(id) {
18530             var oWrapper = this.elementCache[id];
18531             if (!oWrapper || !oWrapper.el) {
18532                 oWrapper = this.elementCache[id] =
18533                     new this.ElementWrapper(Roo.getDom(id));
18534             }
18535             return oWrapper;
18536         },
18537
18538         /**
18539          * Returns the actual DOM element
18540          * @method getElement
18541          * @param {String} id the id of the elment to get
18542          * @return {Object} The element
18543          * @deprecated use Roo.getDom instead
18544          * @static
18545          */
18546         getElement: function(id) {
18547             return Roo.getDom(id);
18548         },
18549
18550         /**
18551          * Returns the style property for the DOM element (i.e.,
18552          * document.getElById(id).style)
18553          * @method getCss
18554          * @param {String} id the id of the elment to get
18555          * @return {Object} The style property of the element
18556          * @deprecated use Roo.getDom instead
18557          * @static
18558          */
18559         getCss: function(id) {
18560             var el = Roo.getDom(id);
18561             return (el) ? el.style : null;
18562         },
18563
18564         /**
18565          * Inner class for cached elements
18566          * @class DragDropMgr.ElementWrapper
18567          * @for DragDropMgr
18568          * @private
18569          * @deprecated
18570          */
18571         ElementWrapper: function(el) {
18572                 /**
18573                  * The element
18574                  * @property el
18575                  */
18576                 this.el = el || null;
18577                 /**
18578                  * The element id
18579                  * @property id
18580                  */
18581                 this.id = this.el && el.id;
18582                 /**
18583                  * A reference to the style property
18584                  * @property css
18585                  */
18586                 this.css = this.el && el.style;
18587             },
18588
18589         /**
18590          * Returns the X position of an html element
18591          * @method getPosX
18592          * @param el the element for which to get the position
18593          * @return {int} the X coordinate
18594          * @for DragDropMgr
18595          * @deprecated use Roo.lib.Dom.getX instead
18596          * @static
18597          */
18598         getPosX: function(el) {
18599             return Roo.lib.Dom.getX(el);
18600         },
18601
18602         /**
18603          * Returns the Y position of an html element
18604          * @method getPosY
18605          * @param el the element for which to get the position
18606          * @return {int} the Y coordinate
18607          * @deprecated use Roo.lib.Dom.getY instead
18608          * @static
18609          */
18610         getPosY: function(el) {
18611             return Roo.lib.Dom.getY(el);
18612         },
18613
18614         /**
18615          * Swap two nodes.  In IE, we use the native method, for others we
18616          * emulate the IE behavior
18617          * @method swapNode
18618          * @param n1 the first node to swap
18619          * @param n2 the other node to swap
18620          * @static
18621          */
18622         swapNode: function(n1, n2) {
18623             if (n1.swapNode) {
18624                 n1.swapNode(n2);
18625             } else {
18626                 var p = n2.parentNode;
18627                 var s = n2.nextSibling;
18628
18629                 if (s == n1) {
18630                     p.insertBefore(n1, n2);
18631                 } else if (n2 == n1.nextSibling) {
18632                     p.insertBefore(n2, n1);
18633                 } else {
18634                     n1.parentNode.replaceChild(n2, n1);
18635                     p.insertBefore(n1, s);
18636                 }
18637             }
18638         },
18639
18640         /**
18641          * Returns the current scroll position
18642          * @method getScroll
18643          * @private
18644          * @static
18645          */
18646         getScroll: function () {
18647             var t, l, dde=document.documentElement, db=document.body;
18648             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18649                 t = dde.scrollTop;
18650                 l = dde.scrollLeft;
18651             } else if (db) {
18652                 t = db.scrollTop;
18653                 l = db.scrollLeft;
18654             } else {
18655
18656             }
18657             return { top: t, left: l };
18658         },
18659
18660         /**
18661          * Returns the specified element style property
18662          * @method getStyle
18663          * @param {HTMLElement} el          the element
18664          * @param {string}      styleProp   the style property
18665          * @return {string} The value of the style property
18666          * @deprecated use Roo.lib.Dom.getStyle
18667          * @static
18668          */
18669         getStyle: function(el, styleProp) {
18670             return Roo.fly(el).getStyle(styleProp);
18671         },
18672
18673         /**
18674          * Gets the scrollTop
18675          * @method getScrollTop
18676          * @return {int} the document's scrollTop
18677          * @static
18678          */
18679         getScrollTop: function () { return this.getScroll().top; },
18680
18681         /**
18682          * Gets the scrollLeft
18683          * @method getScrollLeft
18684          * @return {int} the document's scrollTop
18685          * @static
18686          */
18687         getScrollLeft: function () { return this.getScroll().left; },
18688
18689         /**
18690          * Sets the x/y position of an element to the location of the
18691          * target element.
18692          * @method moveToEl
18693          * @param {HTMLElement} moveEl      The element to move
18694          * @param {HTMLElement} targetEl    The position reference element
18695          * @static
18696          */
18697         moveToEl: function (moveEl, targetEl) {
18698             var aCoord = Roo.lib.Dom.getXY(targetEl);
18699             Roo.lib.Dom.setXY(moveEl, aCoord);
18700         },
18701
18702         /**
18703          * Numeric array sort function
18704          * @method numericSort
18705          * @static
18706          */
18707         numericSort: function(a, b) { return (a - b); },
18708
18709         /**
18710          * Internal counter
18711          * @property _timeoutCount
18712          * @private
18713          * @static
18714          */
18715         _timeoutCount: 0,
18716
18717         /**
18718          * Trying to make the load order less important.  Without this we get
18719          * an error if this file is loaded before the Event Utility.
18720          * @method _addListeners
18721          * @private
18722          * @static
18723          */
18724         _addListeners: function() {
18725             var DDM = Roo.dd.DDM;
18726             if ( Roo.lib.Event && document ) {
18727                 DDM._onLoad();
18728             } else {
18729                 if (DDM._timeoutCount > 2000) {
18730                 } else {
18731                     setTimeout(DDM._addListeners, 10);
18732                     if (document && document.body) {
18733                         DDM._timeoutCount += 1;
18734                     }
18735                 }
18736             }
18737         },
18738
18739         /**
18740          * Recursively searches the immediate parent and all child nodes for
18741          * the handle element in order to determine wheter or not it was
18742          * clicked.
18743          * @method handleWasClicked
18744          * @param node the html element to inspect
18745          * @static
18746          */
18747         handleWasClicked: function(node, id) {
18748             if (this.isHandle(id, node.id)) {
18749                 return true;
18750             } else {
18751                 // check to see if this is a text node child of the one we want
18752                 var p = node.parentNode;
18753
18754                 while (p) {
18755                     if (this.isHandle(id, p.id)) {
18756                         return true;
18757                     } else {
18758                         p = p.parentNode;
18759                     }
18760                 }
18761             }
18762
18763             return false;
18764         }
18765
18766     };
18767
18768 }();
18769
18770 // shorter alias, save a few bytes
18771 Roo.dd.DDM = Roo.dd.DragDropMgr;
18772 Roo.dd.DDM._addListeners();
18773
18774 }/*
18775  * Based on:
18776  * Ext JS Library 1.1.1
18777  * Copyright(c) 2006-2007, Ext JS, LLC.
18778  *
18779  * Originally Released Under LGPL - original licence link has changed is not relivant.
18780  *
18781  * Fork - LGPL
18782  * <script type="text/javascript">
18783  */
18784
18785 /**
18786  * @class Roo.dd.DD
18787  * A DragDrop implementation where the linked element follows the
18788  * mouse cursor during a drag.
18789  * @extends Roo.dd.DragDrop
18790  * @constructor
18791  * @param {String} id the id of the linked element
18792  * @param {String} sGroup the group of related DragDrop items
18793  * @param {object} config an object containing configurable attributes
18794  *                Valid properties for DD:
18795  *                    scroll
18796  */
18797 Roo.dd.DD = function(id, sGroup, config) {
18798     if (id) {
18799         this.init(id, sGroup, config);
18800     }
18801 };
18802
18803 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18804
18805     /**
18806      * When set to true, the utility automatically tries to scroll the browser
18807      * window wehn a drag and drop element is dragged near the viewport boundary.
18808      * Defaults to true.
18809      * @property scroll
18810      * @type boolean
18811      */
18812     scroll: true,
18813
18814     /**
18815      * Sets the pointer offset to the distance between the linked element's top
18816      * left corner and the location the element was clicked
18817      * @method autoOffset
18818      * @param {int} iPageX the X coordinate of the click
18819      * @param {int} iPageY the Y coordinate of the click
18820      */
18821     autoOffset: function(iPageX, iPageY) {
18822         var x = iPageX - this.startPageX;
18823         var y = iPageY - this.startPageY;
18824         this.setDelta(x, y);
18825     },
18826
18827     /**
18828      * Sets the pointer offset.  You can call this directly to force the
18829      * offset to be in a particular location (e.g., pass in 0,0 to set it
18830      * to the center of the object)
18831      * @method setDelta
18832      * @param {int} iDeltaX the distance from the left
18833      * @param {int} iDeltaY the distance from the top
18834      */
18835     setDelta: function(iDeltaX, iDeltaY) {
18836         this.deltaX = iDeltaX;
18837         this.deltaY = iDeltaY;
18838     },
18839
18840     /**
18841      * Sets the drag element to the location of the mousedown or click event,
18842      * maintaining the cursor location relative to the location on the element
18843      * that was clicked.  Override this if you want to place the element in a
18844      * location other than where the cursor is.
18845      * @method setDragElPos
18846      * @param {int} iPageX the X coordinate of the mousedown or drag event
18847      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18848      */
18849     setDragElPos: function(iPageX, iPageY) {
18850         // the first time we do this, we are going to check to make sure
18851         // the element has css positioning
18852
18853         var el = this.getDragEl();
18854         this.alignElWithMouse(el, iPageX, iPageY);
18855     },
18856
18857     /**
18858      * Sets the element to the location of the mousedown or click event,
18859      * maintaining the cursor location relative to the location on the element
18860      * that was clicked.  Override this if you want to place the element in a
18861      * location other than where the cursor is.
18862      * @method alignElWithMouse
18863      * @param {HTMLElement} el the element to move
18864      * @param {int} iPageX the X coordinate of the mousedown or drag event
18865      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18866      */
18867     alignElWithMouse: function(el, iPageX, iPageY) {
18868         var oCoord = this.getTargetCoord(iPageX, iPageY);
18869         var fly = el.dom ? el : Roo.fly(el);
18870         if (!this.deltaSetXY) {
18871             var aCoord = [oCoord.x, oCoord.y];
18872             fly.setXY(aCoord);
18873             var newLeft = fly.getLeft(true);
18874             var newTop  = fly.getTop(true);
18875             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18876         } else {
18877             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18878         }
18879
18880         this.cachePosition(oCoord.x, oCoord.y);
18881         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18882         return oCoord;
18883     },
18884
18885     /**
18886      * Saves the most recent position so that we can reset the constraints and
18887      * tick marks on-demand.  We need to know this so that we can calculate the
18888      * number of pixels the element is offset from its original position.
18889      * @method cachePosition
18890      * @param iPageX the current x position (optional, this just makes it so we
18891      * don't have to look it up again)
18892      * @param iPageY the current y position (optional, this just makes it so we
18893      * don't have to look it up again)
18894      */
18895     cachePosition: function(iPageX, iPageY) {
18896         if (iPageX) {
18897             this.lastPageX = iPageX;
18898             this.lastPageY = iPageY;
18899         } else {
18900             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18901             this.lastPageX = aCoord[0];
18902             this.lastPageY = aCoord[1];
18903         }
18904     },
18905
18906     /**
18907      * Auto-scroll the window if the dragged object has been moved beyond the
18908      * visible window boundary.
18909      * @method autoScroll
18910      * @param {int} x the drag element's x position
18911      * @param {int} y the drag element's y position
18912      * @param {int} h the height of the drag element
18913      * @param {int} w the width of the drag element
18914      * @private
18915      */
18916     autoScroll: function(x, y, h, w) {
18917
18918         if (this.scroll) {
18919             // The client height
18920             var clientH = Roo.lib.Dom.getViewWidth();
18921
18922             // The client width
18923             var clientW = Roo.lib.Dom.getViewHeight();
18924
18925             // The amt scrolled down
18926             var st = this.DDM.getScrollTop();
18927
18928             // The amt scrolled right
18929             var sl = this.DDM.getScrollLeft();
18930
18931             // Location of the bottom of the element
18932             var bot = h + y;
18933
18934             // Location of the right of the element
18935             var right = w + x;
18936
18937             // The distance from the cursor to the bottom of the visible area,
18938             // adjusted so that we don't scroll if the cursor is beyond the
18939             // element drag constraints
18940             var toBot = (clientH + st - y - this.deltaY);
18941
18942             // The distance from the cursor to the right of the visible area
18943             var toRight = (clientW + sl - x - this.deltaX);
18944
18945
18946             // How close to the edge the cursor must be before we scroll
18947             // var thresh = (document.all) ? 100 : 40;
18948             var thresh = 40;
18949
18950             // How many pixels to scroll per autoscroll op.  This helps to reduce
18951             // clunky scrolling. IE is more sensitive about this ... it needs this
18952             // value to be higher.
18953             var scrAmt = (document.all) ? 80 : 30;
18954
18955             // Scroll down if we are near the bottom of the visible page and the
18956             // obj extends below the crease
18957             if ( bot > clientH && toBot < thresh ) {
18958                 window.scrollTo(sl, st + scrAmt);
18959             }
18960
18961             // Scroll up if the window is scrolled down and the top of the object
18962             // goes above the top border
18963             if ( y < st && st > 0 && y - st < thresh ) {
18964                 window.scrollTo(sl, st - scrAmt);
18965             }
18966
18967             // Scroll right if the obj is beyond the right border and the cursor is
18968             // near the border.
18969             if ( right > clientW && toRight < thresh ) {
18970                 window.scrollTo(sl + scrAmt, st);
18971             }
18972
18973             // Scroll left if the window has been scrolled to the right and the obj
18974             // extends past the left border
18975             if ( x < sl && sl > 0 && x - sl < thresh ) {
18976                 window.scrollTo(sl - scrAmt, st);
18977             }
18978         }
18979     },
18980
18981     /**
18982      * Finds the location the element should be placed if we want to move
18983      * it to where the mouse location less the click offset would place us.
18984      * @method getTargetCoord
18985      * @param {int} iPageX the X coordinate of the click
18986      * @param {int} iPageY the Y coordinate of the click
18987      * @return an object that contains the coordinates (Object.x and Object.y)
18988      * @private
18989      */
18990     getTargetCoord: function(iPageX, iPageY) {
18991
18992
18993         var x = iPageX - this.deltaX;
18994         var y = iPageY - this.deltaY;
18995
18996         if (this.constrainX) {
18997             if (x < this.minX) { x = this.minX; }
18998             if (x > this.maxX) { x = this.maxX; }
18999         }
19000
19001         if (this.constrainY) {
19002             if (y < this.minY) { y = this.minY; }
19003             if (y > this.maxY) { y = this.maxY; }
19004         }
19005
19006         x = this.getTick(x, this.xTicks);
19007         y = this.getTick(y, this.yTicks);
19008
19009
19010         return {x:x, y:y};
19011     },
19012
19013     /*
19014      * Sets up config options specific to this class. Overrides
19015      * Roo.dd.DragDrop, but all versions of this method through the
19016      * inheritance chain are called
19017      */
19018     applyConfig: function() {
19019         Roo.dd.DD.superclass.applyConfig.call(this);
19020         this.scroll = (this.config.scroll !== false);
19021     },
19022
19023     /*
19024      * Event that fires prior to the onMouseDown event.  Overrides
19025      * Roo.dd.DragDrop.
19026      */
19027     b4MouseDown: function(e) {
19028         // this.resetConstraints();
19029         this.autoOffset(e.getPageX(),
19030                             e.getPageY());
19031     },
19032
19033     /*
19034      * Event that fires prior to the onDrag event.  Overrides
19035      * Roo.dd.DragDrop.
19036      */
19037     b4Drag: function(e) {
19038         this.setDragElPos(e.getPageX(),
19039                             e.getPageY());
19040     },
19041
19042     toString: function() {
19043         return ("DD " + this.id);
19044     }
19045
19046     //////////////////////////////////////////////////////////////////////////
19047     // Debugging ygDragDrop events that can be overridden
19048     //////////////////////////////////////////////////////////////////////////
19049     /*
19050     startDrag: function(x, y) {
19051     },
19052
19053     onDrag: function(e) {
19054     },
19055
19056     onDragEnter: function(e, id) {
19057     },
19058
19059     onDragOver: function(e, id) {
19060     },
19061
19062     onDragOut: function(e, id) {
19063     },
19064
19065     onDragDrop: function(e, id) {
19066     },
19067
19068     endDrag: function(e) {
19069     }
19070
19071     */
19072
19073 });/*
19074  * Based on:
19075  * Ext JS Library 1.1.1
19076  * Copyright(c) 2006-2007, Ext JS, LLC.
19077  *
19078  * Originally Released Under LGPL - original licence link has changed is not relivant.
19079  *
19080  * Fork - LGPL
19081  * <script type="text/javascript">
19082  */
19083
19084 /**
19085  * @class Roo.dd.DDProxy
19086  * A DragDrop implementation that inserts an empty, bordered div into
19087  * the document that follows the cursor during drag operations.  At the time of
19088  * the click, the frame div is resized to the dimensions of the linked html
19089  * element, and moved to the exact location of the linked element.
19090  *
19091  * References to the "frame" element refer to the single proxy element that
19092  * was created to be dragged in place of all DDProxy elements on the
19093  * page.
19094  *
19095  * @extends Roo.dd.DD
19096  * @constructor
19097  * @param {String} id the id of the linked html element
19098  * @param {String} sGroup the group of related DragDrop objects
19099  * @param {object} config an object containing configurable attributes
19100  *                Valid properties for DDProxy in addition to those in DragDrop:
19101  *                   resizeFrame, centerFrame, dragElId
19102  */
19103 Roo.dd.DDProxy = function(id, sGroup, config) {
19104     if (id) {
19105         this.init(id, sGroup, config);
19106         this.initFrame();
19107     }
19108 };
19109
19110 /**
19111  * The default drag frame div id
19112  * @property Roo.dd.DDProxy.dragElId
19113  * @type String
19114  * @static
19115  */
19116 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19117
19118 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19119
19120     /**
19121      * By default we resize the drag frame to be the same size as the element
19122      * we want to drag (this is to get the frame effect).  We can turn it off
19123      * if we want a different behavior.
19124      * @property resizeFrame
19125      * @type boolean
19126      */
19127     resizeFrame: true,
19128
19129     /**
19130      * By default the frame is positioned exactly where the drag element is, so
19131      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19132      * you do not have constraints on the obj is to have the drag frame centered
19133      * around the cursor.  Set centerFrame to true for this effect.
19134      * @property centerFrame
19135      * @type boolean
19136      */
19137     centerFrame: false,
19138
19139     /**
19140      * Creates the proxy element if it does not yet exist
19141      * @method createFrame
19142      */
19143     createFrame: function() {
19144         var self = this;
19145         var body = document.body;
19146
19147         if (!body || !body.firstChild) {
19148             setTimeout( function() { self.createFrame(); }, 50 );
19149             return;
19150         }
19151
19152         var div = this.getDragEl();
19153
19154         if (!div) {
19155             div    = document.createElement("div");
19156             div.id = this.dragElId;
19157             var s  = div.style;
19158
19159             s.position   = "absolute";
19160             s.visibility = "hidden";
19161             s.cursor     = "move";
19162             s.border     = "2px solid #aaa";
19163             s.zIndex     = 999;
19164
19165             // appendChild can blow up IE if invoked prior to the window load event
19166             // while rendering a table.  It is possible there are other scenarios
19167             // that would cause this to happen as well.
19168             body.insertBefore(div, body.firstChild);
19169         }
19170     },
19171
19172     /**
19173      * Initialization for the drag frame element.  Must be called in the
19174      * constructor of all subclasses
19175      * @method initFrame
19176      */
19177     initFrame: function() {
19178         this.createFrame();
19179     },
19180
19181     applyConfig: function() {
19182         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19183
19184         this.resizeFrame = (this.config.resizeFrame !== false);
19185         this.centerFrame = (this.config.centerFrame);
19186         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19187     },
19188
19189     /**
19190      * Resizes the drag frame to the dimensions of the clicked object, positions
19191      * it over the object, and finally displays it
19192      * @method showFrame
19193      * @param {int} iPageX X click position
19194      * @param {int} iPageY Y click position
19195      * @private
19196      */
19197     showFrame: function(iPageX, iPageY) {
19198         var el = this.getEl();
19199         var dragEl = this.getDragEl();
19200         var s = dragEl.style;
19201
19202         this._resizeProxy();
19203
19204         if (this.centerFrame) {
19205             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19206                            Math.round(parseInt(s.height, 10)/2) );
19207         }
19208
19209         this.setDragElPos(iPageX, iPageY);
19210
19211         Roo.fly(dragEl).show();
19212     },
19213
19214     /**
19215      * The proxy is automatically resized to the dimensions of the linked
19216      * element when a drag is initiated, unless resizeFrame is set to false
19217      * @method _resizeProxy
19218      * @private
19219      */
19220     _resizeProxy: function() {
19221         if (this.resizeFrame) {
19222             var el = this.getEl();
19223             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19224         }
19225     },
19226
19227     // overrides Roo.dd.DragDrop
19228     b4MouseDown: function(e) {
19229         var x = e.getPageX();
19230         var y = e.getPageY();
19231         this.autoOffset(x, y);
19232         this.setDragElPos(x, y);
19233     },
19234
19235     // overrides Roo.dd.DragDrop
19236     b4StartDrag: function(x, y) {
19237         // show the drag frame
19238         this.showFrame(x, y);
19239     },
19240
19241     // overrides Roo.dd.DragDrop
19242     b4EndDrag: function(e) {
19243         Roo.fly(this.getDragEl()).hide();
19244     },
19245
19246     // overrides Roo.dd.DragDrop
19247     // By default we try to move the element to the last location of the frame.
19248     // This is so that the default behavior mirrors that of Roo.dd.DD.
19249     endDrag: function(e) {
19250
19251         var lel = this.getEl();
19252         var del = this.getDragEl();
19253
19254         // Show the drag frame briefly so we can get its position
19255         del.style.visibility = "";
19256
19257         this.beforeMove();
19258         // Hide the linked element before the move to get around a Safari
19259         // rendering bug.
19260         lel.style.visibility = "hidden";
19261         Roo.dd.DDM.moveToEl(lel, del);
19262         del.style.visibility = "hidden";
19263         lel.style.visibility = "";
19264
19265         this.afterDrag();
19266     },
19267
19268     beforeMove : function(){
19269
19270     },
19271
19272     afterDrag : function(){
19273
19274     },
19275
19276     toString: function() {
19277         return ("DDProxy " + this.id);
19278     }
19279
19280 });
19281 /*
19282  * Based on:
19283  * Ext JS Library 1.1.1
19284  * Copyright(c) 2006-2007, Ext JS, LLC.
19285  *
19286  * Originally Released Under LGPL - original licence link has changed is not relivant.
19287  *
19288  * Fork - LGPL
19289  * <script type="text/javascript">
19290  */
19291
19292  /**
19293  * @class Roo.dd.DDTarget
19294  * A DragDrop implementation that does not move, but can be a drop
19295  * target.  You would get the same result by simply omitting implementation
19296  * for the event callbacks, but this way we reduce the processing cost of the
19297  * event listener and the callbacks.
19298  * @extends Roo.dd.DragDrop
19299  * @constructor
19300  * @param {String} id the id of the element that is a drop target
19301  * @param {String} sGroup the group of related DragDrop objects
19302  * @param {object} config an object containing configurable attributes
19303  *                 Valid properties for DDTarget in addition to those in
19304  *                 DragDrop:
19305  *                    none
19306  */
19307 Roo.dd.DDTarget = function(id, sGroup, config) {
19308     if (id) {
19309         this.initTarget(id, sGroup, config);
19310     }
19311     if (config.listeners || config.events) { 
19312        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19313             listeners : config.listeners || {}, 
19314             events : config.events || {} 
19315         });    
19316     }
19317 };
19318
19319 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19320 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19321     toString: function() {
19322         return ("DDTarget " + this.id);
19323     }
19324 });
19325 /*
19326  * Based on:
19327  * Ext JS Library 1.1.1
19328  * Copyright(c) 2006-2007, Ext JS, LLC.
19329  *
19330  * Originally Released Under LGPL - original licence link has changed is not relivant.
19331  *
19332  * Fork - LGPL
19333  * <script type="text/javascript">
19334  */
19335  
19336
19337 /**
19338  * @class Roo.dd.ScrollManager
19339  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19340  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19341  * @singleton
19342  */
19343 Roo.dd.ScrollManager = function(){
19344     var ddm = Roo.dd.DragDropMgr;
19345     var els = {};
19346     var dragEl = null;
19347     var proc = {};
19348     
19349     
19350     
19351     var onStop = function(e){
19352         dragEl = null;
19353         clearProc();
19354     };
19355     
19356     var triggerRefresh = function(){
19357         if(ddm.dragCurrent){
19358              ddm.refreshCache(ddm.dragCurrent.groups);
19359         }
19360     };
19361     
19362     var doScroll = function(){
19363         if(ddm.dragCurrent){
19364             var dds = Roo.dd.ScrollManager;
19365             if(!dds.animate){
19366                 if(proc.el.scroll(proc.dir, dds.increment)){
19367                     triggerRefresh();
19368                 }
19369             }else{
19370                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19371             }
19372         }
19373     };
19374     
19375     var clearProc = function(){
19376         if(proc.id){
19377             clearInterval(proc.id);
19378         }
19379         proc.id = 0;
19380         proc.el = null;
19381         proc.dir = "";
19382     };
19383     
19384     var startProc = function(el, dir){
19385          Roo.log('scroll startproc');
19386         clearProc();
19387         proc.el = el;
19388         proc.dir = dir;
19389         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19390     };
19391     
19392     var onFire = function(e, isDrop){
19393        
19394         if(isDrop || !ddm.dragCurrent){ return; }
19395         var dds = Roo.dd.ScrollManager;
19396         if(!dragEl || dragEl != ddm.dragCurrent){
19397             dragEl = ddm.dragCurrent;
19398             // refresh regions on drag start
19399             dds.refreshCache();
19400         }
19401         
19402         var xy = Roo.lib.Event.getXY(e);
19403         var pt = new Roo.lib.Point(xy[0], xy[1]);
19404         for(var id in els){
19405             var el = els[id], r = el._region;
19406             if(r && r.contains(pt) && el.isScrollable()){
19407                 if(r.bottom - pt.y <= dds.thresh){
19408                     if(proc.el != el){
19409                         startProc(el, "down");
19410                     }
19411                     return;
19412                 }else if(r.right - pt.x <= dds.thresh){
19413                     if(proc.el != el){
19414                         startProc(el, "left");
19415                     }
19416                     return;
19417                 }else if(pt.y - r.top <= dds.thresh){
19418                     if(proc.el != el){
19419                         startProc(el, "up");
19420                     }
19421                     return;
19422                 }else if(pt.x - r.left <= dds.thresh){
19423                     if(proc.el != el){
19424                         startProc(el, "right");
19425                     }
19426                     return;
19427                 }
19428             }
19429         }
19430         clearProc();
19431     };
19432     
19433     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19434     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19435     
19436     return {
19437         /**
19438          * Registers new overflow element(s) to auto scroll
19439          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19440          */
19441         register : function(el){
19442             if(el instanceof Array){
19443                 for(var i = 0, len = el.length; i < len; i++) {
19444                         this.register(el[i]);
19445                 }
19446             }else{
19447                 el = Roo.get(el);
19448                 els[el.id] = el;
19449             }
19450             Roo.dd.ScrollManager.els = els;
19451         },
19452         
19453         /**
19454          * Unregisters overflow element(s) so they are no longer scrolled
19455          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19456          */
19457         unregister : function(el){
19458             if(el instanceof Array){
19459                 for(var i = 0, len = el.length; i < len; i++) {
19460                         this.unregister(el[i]);
19461                 }
19462             }else{
19463                 el = Roo.get(el);
19464                 delete els[el.id];
19465             }
19466         },
19467         
19468         /**
19469          * The number of pixels from the edge of a container the pointer needs to be to 
19470          * trigger scrolling (defaults to 25)
19471          * @type Number
19472          */
19473         thresh : 25,
19474         
19475         /**
19476          * The number of pixels to scroll in each scroll increment (defaults to 50)
19477          * @type Number
19478          */
19479         increment : 100,
19480         
19481         /**
19482          * The frequency of scrolls in milliseconds (defaults to 500)
19483          * @type Number
19484          */
19485         frequency : 500,
19486         
19487         /**
19488          * True to animate the scroll (defaults to true)
19489          * @type Boolean
19490          */
19491         animate: true,
19492         
19493         /**
19494          * The animation duration in seconds - 
19495          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19496          * @type Number
19497          */
19498         animDuration: .4,
19499         
19500         /**
19501          * Manually trigger a cache refresh.
19502          */
19503         refreshCache : function(){
19504             for(var id in els){
19505                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19506                     els[id]._region = els[id].getRegion();
19507                 }
19508             }
19509         }
19510     };
19511 }();/*
19512  * Based on:
19513  * Ext JS Library 1.1.1
19514  * Copyright(c) 2006-2007, Ext JS, LLC.
19515  *
19516  * Originally Released Under LGPL - original licence link has changed is not relivant.
19517  *
19518  * Fork - LGPL
19519  * <script type="text/javascript">
19520  */
19521  
19522
19523 /**
19524  * @class Roo.dd.Registry
19525  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19526  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19527  * @singleton
19528  */
19529 Roo.dd.Registry = function(){
19530     var elements = {}; 
19531     var handles = {}; 
19532     var autoIdSeed = 0;
19533
19534     var getId = function(el, autogen){
19535         if(typeof el == "string"){
19536             return el;
19537         }
19538         var id = el.id;
19539         if(!id && autogen !== false){
19540             id = "roodd-" + (++autoIdSeed);
19541             el.id = id;
19542         }
19543         return id;
19544     };
19545     
19546     return {
19547     /**
19548      * Register a drag drop element
19549      * @param {String|HTMLElement} element The id or DOM node to register
19550      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19551      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19552      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19553      * populated in the data object (if applicable):
19554      * <pre>
19555 Value      Description<br />
19556 ---------  ------------------------------------------<br />
19557 handles    Array of DOM nodes that trigger dragging<br />
19558            for the element being registered<br />
19559 isHandle   True if the element passed in triggers<br />
19560            dragging itself, else false
19561 </pre>
19562      */
19563         register : function(el, data){
19564             data = data || {};
19565             if(typeof el == "string"){
19566                 el = document.getElementById(el);
19567             }
19568             data.ddel = el;
19569             elements[getId(el)] = data;
19570             if(data.isHandle !== false){
19571                 handles[data.ddel.id] = data;
19572             }
19573             if(data.handles){
19574                 var hs = data.handles;
19575                 for(var i = 0, len = hs.length; i < len; i++){
19576                         handles[getId(hs[i])] = data;
19577                 }
19578             }
19579         },
19580
19581     /**
19582      * Unregister a drag drop element
19583      * @param {String|HTMLElement}  element The id or DOM node to unregister
19584      */
19585         unregister : function(el){
19586             var id = getId(el, false);
19587             var data = elements[id];
19588             if(data){
19589                 delete elements[id];
19590                 if(data.handles){
19591                     var hs = data.handles;
19592                     for(var i = 0, len = hs.length; i < len; i++){
19593                         delete handles[getId(hs[i], false)];
19594                     }
19595                 }
19596             }
19597         },
19598
19599     /**
19600      * Returns the handle registered for a DOM Node by id
19601      * @param {String|HTMLElement} id The DOM node or id to look up
19602      * @return {Object} handle The custom handle data
19603      */
19604         getHandle : function(id){
19605             if(typeof id != "string"){ // must be element?
19606                 id = id.id;
19607             }
19608             return handles[id];
19609         },
19610
19611     /**
19612      * Returns the handle that is registered for the DOM node that is the target of the event
19613      * @param {Event} e The event
19614      * @return {Object} handle The custom handle data
19615      */
19616         getHandleFromEvent : function(e){
19617             var t = Roo.lib.Event.getTarget(e);
19618             return t ? handles[t.id] : null;
19619         },
19620
19621     /**
19622      * Returns a custom data object that is registered for a DOM node by id
19623      * @param {String|HTMLElement} id The DOM node or id to look up
19624      * @return {Object} data The custom data
19625      */
19626         getTarget : function(id){
19627             if(typeof id != "string"){ // must be element?
19628                 id = id.id;
19629             }
19630             return elements[id];
19631         },
19632
19633     /**
19634      * Returns a custom data object that is registered for the DOM node that is the target of the event
19635      * @param {Event} e The event
19636      * @return {Object} data The custom data
19637      */
19638         getTargetFromEvent : function(e){
19639             var t = Roo.lib.Event.getTarget(e);
19640             return t ? elements[t.id] || handles[t.id] : null;
19641         }
19642     };
19643 }();/*
19644  * Based on:
19645  * Ext JS Library 1.1.1
19646  * Copyright(c) 2006-2007, Ext JS, LLC.
19647  *
19648  * Originally Released Under LGPL - original licence link has changed is not relivant.
19649  *
19650  * Fork - LGPL
19651  * <script type="text/javascript">
19652  */
19653  
19654
19655 /**
19656  * @class Roo.dd.StatusProxy
19657  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19658  * default drag proxy used by all Roo.dd components.
19659  * @constructor
19660  * @param {Object} config
19661  */
19662 Roo.dd.StatusProxy = function(config){
19663     Roo.apply(this, config);
19664     this.id = this.id || Roo.id();
19665     this.el = new Roo.Layer({
19666         dh: {
19667             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19668                 {tag: "div", cls: "x-dd-drop-icon"},
19669                 {tag: "div", cls: "x-dd-drag-ghost"}
19670             ]
19671         }, 
19672         shadow: !config || config.shadow !== false
19673     });
19674     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19675     this.dropStatus = this.dropNotAllowed;
19676 };
19677
19678 Roo.dd.StatusProxy.prototype = {
19679     /**
19680      * @cfg {String} dropAllowed
19681      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19682      */
19683     dropAllowed : "x-dd-drop-ok",
19684     /**
19685      * @cfg {String} dropNotAllowed
19686      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19687      */
19688     dropNotAllowed : "x-dd-drop-nodrop",
19689
19690     /**
19691      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19692      * over the current target element.
19693      * @param {String} cssClass The css class for the new drop status indicator image
19694      */
19695     setStatus : function(cssClass){
19696         cssClass = cssClass || this.dropNotAllowed;
19697         if(this.dropStatus != cssClass){
19698             this.el.replaceClass(this.dropStatus, cssClass);
19699             this.dropStatus = cssClass;
19700         }
19701     },
19702
19703     /**
19704      * Resets the status indicator to the default dropNotAllowed value
19705      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19706      */
19707     reset : function(clearGhost){
19708         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19709         this.dropStatus = this.dropNotAllowed;
19710         if(clearGhost){
19711             this.ghost.update("");
19712         }
19713     },
19714
19715     /**
19716      * Updates the contents of the ghost element
19717      * @param {String} html The html that will replace the current innerHTML of the ghost element
19718      */
19719     update : function(html){
19720         if(typeof html == "string"){
19721             this.ghost.update(html);
19722         }else{
19723             this.ghost.update("");
19724             html.style.margin = "0";
19725             this.ghost.dom.appendChild(html);
19726         }
19727         // ensure float = none set?? cant remember why though.
19728         var el = this.ghost.dom.firstChild;
19729                 if(el){
19730                         Roo.fly(el).setStyle('float', 'none');
19731                 }
19732     },
19733     
19734     /**
19735      * Returns the underlying proxy {@link Roo.Layer}
19736      * @return {Roo.Layer} el
19737     */
19738     getEl : function(){
19739         return this.el;
19740     },
19741
19742     /**
19743      * Returns the ghost element
19744      * @return {Roo.Element} el
19745      */
19746     getGhost : function(){
19747         return this.ghost;
19748     },
19749
19750     /**
19751      * Hides the proxy
19752      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19753      */
19754     hide : function(clear){
19755         this.el.hide();
19756         if(clear){
19757             this.reset(true);
19758         }
19759     },
19760
19761     /**
19762      * Stops the repair animation if it's currently running
19763      */
19764     stop : function(){
19765         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19766             this.anim.stop();
19767         }
19768     },
19769
19770     /**
19771      * Displays this proxy
19772      */
19773     show : function(){
19774         this.el.show();
19775     },
19776
19777     /**
19778      * Force the Layer to sync its shadow and shim positions to the element
19779      */
19780     sync : function(){
19781         this.el.sync();
19782     },
19783
19784     /**
19785      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19786      * invalid drop operation by the item being dragged.
19787      * @param {Array} xy The XY position of the element ([x, y])
19788      * @param {Function} callback The function to call after the repair is complete
19789      * @param {Object} scope The scope in which to execute the callback
19790      */
19791     repair : function(xy, callback, scope){
19792         this.callback = callback;
19793         this.scope = scope;
19794         if(xy && this.animRepair !== false){
19795             this.el.addClass("x-dd-drag-repair");
19796             this.el.hideUnders(true);
19797             this.anim = this.el.shift({
19798                 duration: this.repairDuration || .5,
19799                 easing: 'easeOut',
19800                 xy: xy,
19801                 stopFx: true,
19802                 callback: this.afterRepair,
19803                 scope: this
19804             });
19805         }else{
19806             this.afterRepair();
19807         }
19808     },
19809
19810     // private
19811     afterRepair : function(){
19812         this.hide(true);
19813         if(typeof this.callback == "function"){
19814             this.callback.call(this.scope || this);
19815         }
19816         this.callback = null;
19817         this.scope = null;
19818     }
19819 };/*
19820  * Based on:
19821  * Ext JS Library 1.1.1
19822  * Copyright(c) 2006-2007, Ext JS, LLC.
19823  *
19824  * Originally Released Under LGPL - original licence link has changed is not relivant.
19825  *
19826  * Fork - LGPL
19827  * <script type="text/javascript">
19828  */
19829
19830 /**
19831  * @class Roo.dd.DragSource
19832  * @extends Roo.dd.DDProxy
19833  * A simple class that provides the basic implementation needed to make any element draggable.
19834  * @constructor
19835  * @param {String/HTMLElement/Element} el The container element
19836  * @param {Object} config
19837  */
19838 Roo.dd.DragSource = function(el, config){
19839     this.el = Roo.get(el);
19840     this.dragData = {};
19841     
19842     Roo.apply(this, config);
19843     
19844     if(!this.proxy){
19845         this.proxy = new Roo.dd.StatusProxy();
19846     }
19847
19848     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19849           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19850     
19851     this.dragging = false;
19852 };
19853
19854 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19855     /**
19856      * @cfg {String} dropAllowed
19857      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19858      */
19859     dropAllowed : "x-dd-drop-ok",
19860     /**
19861      * @cfg {String} dropNotAllowed
19862      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19863      */
19864     dropNotAllowed : "x-dd-drop-nodrop",
19865
19866     /**
19867      * Returns the data object associated with this drag source
19868      * @return {Object} data An object containing arbitrary data
19869      */
19870     getDragData : function(e){
19871         return this.dragData;
19872     },
19873
19874     // private
19875     onDragEnter : function(e, id){
19876         var target = Roo.dd.DragDropMgr.getDDById(id);
19877         this.cachedTarget = target;
19878         if(this.beforeDragEnter(target, e, id) !== false){
19879             if(target.isNotifyTarget){
19880                 var status = target.notifyEnter(this, e, this.dragData);
19881                 this.proxy.setStatus(status);
19882             }else{
19883                 this.proxy.setStatus(this.dropAllowed);
19884             }
19885             
19886             if(this.afterDragEnter){
19887                 /**
19888                  * An empty function by default, but provided so that you can perform a custom action
19889                  * when the dragged item enters the drop target by providing an implementation.
19890                  * @param {Roo.dd.DragDrop} target The drop target
19891                  * @param {Event} e The event object
19892                  * @param {String} id The id of the dragged element
19893                  * @method afterDragEnter
19894                  */
19895                 this.afterDragEnter(target, e, id);
19896             }
19897         }
19898     },
19899
19900     /**
19901      * An empty function by default, but provided so that you can perform a custom action
19902      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19903      * @param {Roo.dd.DragDrop} target The drop target
19904      * @param {Event} e The event object
19905      * @param {String} id The id of the dragged element
19906      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19907      */
19908     beforeDragEnter : function(target, e, id){
19909         return true;
19910     },
19911
19912     // private
19913     alignElWithMouse: function() {
19914         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19915         this.proxy.sync();
19916     },
19917
19918     // private
19919     onDragOver : function(e, id){
19920         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19921         if(this.beforeDragOver(target, e, id) !== false){
19922             if(target.isNotifyTarget){
19923                 var status = target.notifyOver(this, e, this.dragData);
19924                 this.proxy.setStatus(status);
19925             }
19926
19927             if(this.afterDragOver){
19928                 /**
19929                  * An empty function by default, but provided so that you can perform a custom action
19930                  * while the dragged item is over the drop target by providing an implementation.
19931                  * @param {Roo.dd.DragDrop} target The drop target
19932                  * @param {Event} e The event object
19933                  * @param {String} id The id of the dragged element
19934                  * @method afterDragOver
19935                  */
19936                 this.afterDragOver(target, e, id);
19937             }
19938         }
19939     },
19940
19941     /**
19942      * An empty function by default, but provided so that you can perform a custom action
19943      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19944      * @param {Roo.dd.DragDrop} target The drop target
19945      * @param {Event} e The event object
19946      * @param {String} id The id of the dragged element
19947      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19948      */
19949     beforeDragOver : function(target, e, id){
19950         return true;
19951     },
19952
19953     // private
19954     onDragOut : function(e, id){
19955         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19956         if(this.beforeDragOut(target, e, id) !== false){
19957             if(target.isNotifyTarget){
19958                 target.notifyOut(this, e, this.dragData);
19959             }
19960             this.proxy.reset();
19961             if(this.afterDragOut){
19962                 /**
19963                  * An empty function by default, but provided so that you can perform a custom action
19964                  * after the dragged item is dragged out of the target without dropping.
19965                  * @param {Roo.dd.DragDrop} target The drop target
19966                  * @param {Event} e The event object
19967                  * @param {String} id The id of the dragged element
19968                  * @method afterDragOut
19969                  */
19970                 this.afterDragOut(target, e, id);
19971             }
19972         }
19973         this.cachedTarget = null;
19974     },
19975
19976     /**
19977      * An empty function by default, but provided so that you can perform a custom action before the dragged
19978      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
19979      * @param {Roo.dd.DragDrop} target The drop target
19980      * @param {Event} e The event object
19981      * @param {String} id The id of the dragged element
19982      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19983      */
19984     beforeDragOut : function(target, e, id){
19985         return true;
19986     },
19987     
19988     // private
19989     onDragDrop : function(e, id){
19990         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19991         if(this.beforeDragDrop(target, e, id) !== false){
19992             if(target.isNotifyTarget){
19993                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
19994                     this.onValidDrop(target, e, id);
19995                 }else{
19996                     this.onInvalidDrop(target, e, id);
19997                 }
19998             }else{
19999                 this.onValidDrop(target, e, id);
20000             }
20001             
20002             if(this.afterDragDrop){
20003                 /**
20004                  * An empty function by default, but provided so that you can perform a custom action
20005                  * after a valid drag drop has occurred by providing an implementation.
20006                  * @param {Roo.dd.DragDrop} target The drop target
20007                  * @param {Event} e The event object
20008                  * @param {String} id The id of the dropped element
20009                  * @method afterDragDrop
20010                  */
20011                 this.afterDragDrop(target, e, id);
20012             }
20013         }
20014         delete this.cachedTarget;
20015     },
20016
20017     /**
20018      * An empty function by default, but provided so that you can perform a custom action before the dragged
20019      * item is dropped onto the target and optionally cancel the onDragDrop.
20020      * @param {Roo.dd.DragDrop} target The drop target
20021      * @param {Event} e The event object
20022      * @param {String} id The id of the dragged element
20023      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20024      */
20025     beforeDragDrop : function(target, e, id){
20026         return true;
20027     },
20028
20029     // private
20030     onValidDrop : function(target, e, id){
20031         this.hideProxy();
20032         if(this.afterValidDrop){
20033             /**
20034              * An empty function by default, but provided so that you can perform a custom action
20035              * after a valid drop has occurred by providing an implementation.
20036              * @param {Object} target The target DD 
20037              * @param {Event} e The event object
20038              * @param {String} id The id of the dropped element
20039              * @method afterInvalidDrop
20040              */
20041             this.afterValidDrop(target, e, id);
20042         }
20043     },
20044
20045     // private
20046     getRepairXY : function(e, data){
20047         return this.el.getXY();  
20048     },
20049
20050     // private
20051     onInvalidDrop : function(target, e, id){
20052         this.beforeInvalidDrop(target, e, id);
20053         if(this.cachedTarget){
20054             if(this.cachedTarget.isNotifyTarget){
20055                 this.cachedTarget.notifyOut(this, e, this.dragData);
20056             }
20057             this.cacheTarget = null;
20058         }
20059         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20060
20061         if(this.afterInvalidDrop){
20062             /**
20063              * An empty function by default, but provided so that you can perform a custom action
20064              * after an invalid drop has occurred by providing an implementation.
20065              * @param {Event} e The event object
20066              * @param {String} id The id of the dropped element
20067              * @method afterInvalidDrop
20068              */
20069             this.afterInvalidDrop(e, id);
20070         }
20071     },
20072
20073     // private
20074     afterRepair : function(){
20075         if(Roo.enableFx){
20076             this.el.highlight(this.hlColor || "c3daf9");
20077         }
20078         this.dragging = false;
20079     },
20080
20081     /**
20082      * An empty function by default, but provided so that you can perform a custom action after an invalid
20083      * drop has occurred.
20084      * @param {Roo.dd.DragDrop} target The drop target
20085      * @param {Event} e The event object
20086      * @param {String} id The id of the dragged element
20087      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20088      */
20089     beforeInvalidDrop : function(target, e, id){
20090         return true;
20091     },
20092
20093     // private
20094     handleMouseDown : function(e){
20095         if(this.dragging) {
20096             return;
20097         }
20098         var data = this.getDragData(e);
20099         if(data && this.onBeforeDrag(data, e) !== false){
20100             this.dragData = data;
20101             this.proxy.stop();
20102             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20103         } 
20104     },
20105
20106     /**
20107      * An empty function by default, but provided so that you can perform a custom action before the initial
20108      * drag event begins and optionally cancel it.
20109      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20110      * @param {Event} e The event object
20111      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20112      */
20113     onBeforeDrag : function(data, e){
20114         return true;
20115     },
20116
20117     /**
20118      * An empty function by default, but provided so that you can perform a custom action once the initial
20119      * drag event has begun.  The drag cannot be canceled from this function.
20120      * @param {Number} x The x position of the click on the dragged object
20121      * @param {Number} y The y position of the click on the dragged object
20122      */
20123     onStartDrag : Roo.emptyFn,
20124
20125     // private - YUI override
20126     startDrag : function(x, y){
20127         this.proxy.reset();
20128         this.dragging = true;
20129         this.proxy.update("");
20130         this.onInitDrag(x, y);
20131         this.proxy.show();
20132     },
20133
20134     // private
20135     onInitDrag : function(x, y){
20136         var clone = this.el.dom.cloneNode(true);
20137         clone.id = Roo.id(); // prevent duplicate ids
20138         this.proxy.update(clone);
20139         this.onStartDrag(x, y);
20140         return true;
20141     },
20142
20143     /**
20144      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20145      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20146      */
20147     getProxy : function(){
20148         return this.proxy;  
20149     },
20150
20151     /**
20152      * Hides the drag source's {@link Roo.dd.StatusProxy}
20153      */
20154     hideProxy : function(){
20155         this.proxy.hide();  
20156         this.proxy.reset(true);
20157         this.dragging = false;
20158     },
20159
20160     // private
20161     triggerCacheRefresh : function(){
20162         Roo.dd.DDM.refreshCache(this.groups);
20163     },
20164
20165     // private - override to prevent hiding
20166     b4EndDrag: function(e) {
20167     },
20168
20169     // private - override to prevent moving
20170     endDrag : function(e){
20171         this.onEndDrag(this.dragData, e);
20172     },
20173
20174     // private
20175     onEndDrag : function(data, e){
20176     },
20177     
20178     // private - pin to cursor
20179     autoOffset : function(x, y) {
20180         this.setDelta(-12, -20);
20181     }    
20182 });/*
20183  * Based on:
20184  * Ext JS Library 1.1.1
20185  * Copyright(c) 2006-2007, Ext JS, LLC.
20186  *
20187  * Originally Released Under LGPL - original licence link has changed is not relivant.
20188  *
20189  * Fork - LGPL
20190  * <script type="text/javascript">
20191  */
20192
20193
20194 /**
20195  * @class Roo.dd.DropTarget
20196  * @extends Roo.dd.DDTarget
20197  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20198  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20199  * @constructor
20200  * @param {String/HTMLElement/Element} el The container element
20201  * @param {Object} config
20202  */
20203 Roo.dd.DropTarget = function(el, config){
20204     this.el = Roo.get(el);
20205     
20206     var listeners = false; ;
20207     if (config && config.listeners) {
20208         listeners= config.listeners;
20209         delete config.listeners;
20210     }
20211     Roo.apply(this, config);
20212     
20213     if(this.containerScroll){
20214         Roo.dd.ScrollManager.register(this.el);
20215     }
20216     this.addEvents( {
20217          /**
20218          * @scope Roo.dd.DropTarget
20219          */
20220          
20221          /**
20222          * @event enter
20223          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20224          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20225          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20226          * 
20227          * IMPORTANT : it should set this.overClass and this.dropAllowed
20228          * 
20229          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20230          * @param {Event} e The event
20231          * @param {Object} data An object containing arbitrary data supplied by the drag source
20232          */
20233         "enter" : true,
20234         
20235          /**
20236          * @event over
20237          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20238          * This method will be called on every mouse movement while the drag source is over the drop target.
20239          * This default implementation simply returns the dropAllowed config value.
20240          * 
20241          * IMPORTANT : it should set this.dropAllowed
20242          * 
20243          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20244          * @param {Event} e The event
20245          * @param {Object} data An object containing arbitrary data supplied by the drag source
20246          
20247          */
20248         "over" : true,
20249         /**
20250          * @event out
20251          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20252          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20253          * overClass (if any) from the drop element.
20254          * 
20255          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20256          * @param {Event} e The event
20257          * @param {Object} data An object containing arbitrary data supplied by the drag source
20258          */
20259          "out" : true,
20260          
20261         /**
20262          * @event drop
20263          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20264          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20265          * implementation that does something to process the drop event and returns true so that the drag source's
20266          * repair action does not run.
20267          * 
20268          * IMPORTANT : it should set this.success
20269          * 
20270          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20271          * @param {Event} e The event
20272          * @param {Object} data An object containing arbitrary data supplied by the drag source
20273         */
20274          "drop" : true
20275     });
20276             
20277      
20278     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20279         this.el.dom, 
20280         this.ddGroup || this.group,
20281         {
20282             isTarget: true,
20283             listeners : listeners || {} 
20284            
20285         
20286         }
20287     );
20288
20289 };
20290
20291 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20292     /**
20293      * @cfg {String} overClass
20294      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20295      */
20296      /**
20297      * @cfg {String} ddGroup
20298      * The drag drop group to handle drop events for
20299      */
20300      
20301     /**
20302      * @cfg {String} dropAllowed
20303      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20304      */
20305     dropAllowed : "x-dd-drop-ok",
20306     /**
20307      * @cfg {String} dropNotAllowed
20308      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20309      */
20310     dropNotAllowed : "x-dd-drop-nodrop",
20311     /**
20312      * @cfg {boolean} success
20313      * set this after drop listener.. 
20314      */
20315     success : false,
20316     /**
20317      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20318      * if the drop point is valid for over/enter..
20319      */
20320     valid : false,
20321     // private
20322     isTarget : true,
20323
20324     // private
20325     isNotifyTarget : true,
20326     
20327     /**
20328      * @hide
20329      */
20330     notifyEnter : function(dd, e, data)
20331     {
20332         this.valid = true;
20333         this.fireEvent('enter', dd, e, data);
20334         if(this.overClass){
20335             this.el.addClass(this.overClass);
20336         }
20337         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20338             this.valid ? this.dropAllowed : this.dropNotAllowed
20339         );
20340     },
20341
20342     /**
20343      * @hide
20344      */
20345     notifyOver : function(dd, e, data)
20346     {
20347         this.valid = true;
20348         this.fireEvent('over', dd, e, data);
20349         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20350             this.valid ? this.dropAllowed : this.dropNotAllowed
20351         );
20352     },
20353
20354     /**
20355      * @hide
20356      */
20357     notifyOut : function(dd, e, data)
20358     {
20359         this.fireEvent('out', dd, e, data);
20360         if(this.overClass){
20361             this.el.removeClass(this.overClass);
20362         }
20363     },
20364
20365     /**
20366      * @hide
20367      */
20368     notifyDrop : function(dd, e, data)
20369     {
20370         this.success = false;
20371         this.fireEvent('drop', dd, e, data);
20372         return this.success;
20373     }
20374 });/*
20375  * Based on:
20376  * Ext JS Library 1.1.1
20377  * Copyright(c) 2006-2007, Ext JS, LLC.
20378  *
20379  * Originally Released Under LGPL - original licence link has changed is not relivant.
20380  *
20381  * Fork - LGPL
20382  * <script type="text/javascript">
20383  */
20384
20385
20386 /**
20387  * @class Roo.dd.DragZone
20388  * @extends Roo.dd.DragSource
20389  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20390  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20391  * @constructor
20392  * @param {String/HTMLElement/Element} el The container element
20393  * @param {Object} config
20394  */
20395 Roo.dd.DragZone = function(el, config){
20396     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20397     if(this.containerScroll){
20398         Roo.dd.ScrollManager.register(this.el);
20399     }
20400 };
20401
20402 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20403     /**
20404      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20405      * for auto scrolling during drag operations.
20406      */
20407     /**
20408      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20409      * method after a failed drop (defaults to "c3daf9" - light blue)
20410      */
20411
20412     /**
20413      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20414      * for a valid target to drag based on the mouse down. Override this method
20415      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20416      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20417      * @param {EventObject} e The mouse down event
20418      * @return {Object} The dragData
20419      */
20420     getDragData : function(e){
20421         return Roo.dd.Registry.getHandleFromEvent(e);
20422     },
20423     
20424     /**
20425      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20426      * this.dragData.ddel
20427      * @param {Number} x The x position of the click on the dragged object
20428      * @param {Number} y The y position of the click on the dragged object
20429      * @return {Boolean} true to continue the drag, false to cancel
20430      */
20431     onInitDrag : function(x, y){
20432         this.proxy.update(this.dragData.ddel.cloneNode(true));
20433         this.onStartDrag(x, y);
20434         return true;
20435     },
20436     
20437     /**
20438      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20439      */
20440     afterRepair : function(){
20441         if(Roo.enableFx){
20442             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20443         }
20444         this.dragging = false;
20445     },
20446
20447     /**
20448      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20449      * the XY of this.dragData.ddel
20450      * @param {EventObject} e The mouse up event
20451      * @return {Array} The xy location (e.g. [100, 200])
20452      */
20453     getRepairXY : function(e){
20454         return Roo.Element.fly(this.dragData.ddel).getXY();  
20455     }
20456 });/*
20457  * Based on:
20458  * Ext JS Library 1.1.1
20459  * Copyright(c) 2006-2007, Ext JS, LLC.
20460  *
20461  * Originally Released Under LGPL - original licence link has changed is not relivant.
20462  *
20463  * Fork - LGPL
20464  * <script type="text/javascript">
20465  */
20466 /**
20467  * @class Roo.dd.DropZone
20468  * @extends Roo.dd.DropTarget
20469  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20470  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20471  * @constructor
20472  * @param {String/HTMLElement/Element} el The container element
20473  * @param {Object} config
20474  */
20475 Roo.dd.DropZone = function(el, config){
20476     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20477 };
20478
20479 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20480     /**
20481      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20482      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20483      * provide your own custom lookup.
20484      * @param {Event} e The event
20485      * @return {Object} data The custom data
20486      */
20487     getTargetFromEvent : function(e){
20488         return Roo.dd.Registry.getTargetFromEvent(e);
20489     },
20490
20491     /**
20492      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20493      * that it has registered.  This method has no default implementation and should be overridden to provide
20494      * node-specific processing if necessary.
20495      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20496      * {@link #getTargetFromEvent} for this node)
20497      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20498      * @param {Event} e The event
20499      * @param {Object} data An object containing arbitrary data supplied by the drag source
20500      */
20501     onNodeEnter : function(n, dd, e, data){
20502         
20503     },
20504
20505     /**
20506      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20507      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20508      * overridden to provide the proper feedback.
20509      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20510      * {@link #getTargetFromEvent} for this node)
20511      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20512      * @param {Event} e The event
20513      * @param {Object} data An object containing arbitrary data supplied by the drag source
20514      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20515      * underlying {@link Roo.dd.StatusProxy} can be updated
20516      */
20517     onNodeOver : function(n, dd, e, data){
20518         return this.dropAllowed;
20519     },
20520
20521     /**
20522      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20523      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20524      * node-specific processing if necessary.
20525      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20526      * {@link #getTargetFromEvent} for this node)
20527      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20528      * @param {Event} e The event
20529      * @param {Object} data An object containing arbitrary data supplied by the drag source
20530      */
20531     onNodeOut : function(n, dd, e, data){
20532         
20533     },
20534
20535     /**
20536      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20537      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20538      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20539      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20540      * {@link #getTargetFromEvent} for this node)
20541      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20542      * @param {Event} e The event
20543      * @param {Object} data An object containing arbitrary data supplied by the drag source
20544      * @return {Boolean} True if the drop was valid, else false
20545      */
20546     onNodeDrop : function(n, dd, e, data){
20547         return false;
20548     },
20549
20550     /**
20551      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20552      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20553      * it should be overridden to provide the proper feedback if necessary.
20554      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20555      * @param {Event} e The event
20556      * @param {Object} data An object containing arbitrary data supplied by the drag source
20557      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20558      * underlying {@link Roo.dd.StatusProxy} can be updated
20559      */
20560     onContainerOver : function(dd, e, data){
20561         return this.dropNotAllowed;
20562     },
20563
20564     /**
20565      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20566      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20567      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20568      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20569      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20570      * @param {Event} e The event
20571      * @param {Object} data An object containing arbitrary data supplied by the drag source
20572      * @return {Boolean} True if the drop was valid, else false
20573      */
20574     onContainerDrop : function(dd, e, data){
20575         return false;
20576     },
20577
20578     /**
20579      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20580      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20581      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20582      * you should override this method and provide a custom implementation.
20583      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20584      * @param {Event} e The event
20585      * @param {Object} data An object containing arbitrary data supplied by the drag source
20586      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20587      * underlying {@link Roo.dd.StatusProxy} can be updated
20588      */
20589     notifyEnter : function(dd, e, data){
20590         return this.dropNotAllowed;
20591     },
20592
20593     /**
20594      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20595      * This method will be called on every mouse movement while the drag source is over the drop zone.
20596      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20597      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20598      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20599      * registered node, it will call {@link #onContainerOver}.
20600      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20601      * @param {Event} e The event
20602      * @param {Object} data An object containing arbitrary data supplied by the drag source
20603      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20604      * underlying {@link Roo.dd.StatusProxy} can be updated
20605      */
20606     notifyOver : function(dd, e, data){
20607         var n = this.getTargetFromEvent(e);
20608         if(!n){ // not over valid drop target
20609             if(this.lastOverNode){
20610                 this.onNodeOut(this.lastOverNode, dd, e, data);
20611                 this.lastOverNode = null;
20612             }
20613             return this.onContainerOver(dd, e, data);
20614         }
20615         if(this.lastOverNode != n){
20616             if(this.lastOverNode){
20617                 this.onNodeOut(this.lastOverNode, dd, e, data);
20618             }
20619             this.onNodeEnter(n, dd, e, data);
20620             this.lastOverNode = n;
20621         }
20622         return this.onNodeOver(n, dd, e, data);
20623     },
20624
20625     /**
20626      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20627      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20628      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20629      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20630      * @param {Event} e The event
20631      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20632      */
20633     notifyOut : function(dd, e, data){
20634         if(this.lastOverNode){
20635             this.onNodeOut(this.lastOverNode, dd, e, data);
20636             this.lastOverNode = null;
20637         }
20638     },
20639
20640     /**
20641      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20642      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20643      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20644      * otherwise it will call {@link #onContainerDrop}.
20645      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20646      * @param {Event} e The event
20647      * @param {Object} data An object containing arbitrary data supplied by the drag source
20648      * @return {Boolean} True if the drop was valid, else false
20649      */
20650     notifyDrop : function(dd, e, data){
20651         if(this.lastOverNode){
20652             this.onNodeOut(this.lastOverNode, dd, e, data);
20653             this.lastOverNode = null;
20654         }
20655         var n = this.getTargetFromEvent(e);
20656         return n ?
20657             this.onNodeDrop(n, dd, e, data) :
20658             this.onContainerDrop(dd, e, data);
20659     },
20660
20661     // private
20662     triggerCacheRefresh : function(){
20663         Roo.dd.DDM.refreshCache(this.groups);
20664     }  
20665 });/*
20666  * Based on:
20667  * Ext JS Library 1.1.1
20668  * Copyright(c) 2006-2007, Ext JS, LLC.
20669  *
20670  * Originally Released Under LGPL - original licence link has changed is not relivant.
20671  *
20672  * Fork - LGPL
20673  * <script type="text/javascript">
20674  */
20675
20676
20677 /**
20678  * @class Roo.data.SortTypes
20679  * @singleton
20680  * Defines the default sorting (casting?) comparison functions used when sorting data.
20681  */
20682 Roo.data.SortTypes = {
20683     /**
20684      * Default sort that does nothing
20685      * @param {Mixed} s The value being converted
20686      * @return {Mixed} The comparison value
20687      */
20688     none : function(s){
20689         return s;
20690     },
20691     
20692     /**
20693      * The regular expression used to strip tags
20694      * @type {RegExp}
20695      * @property
20696      */
20697     stripTagsRE : /<\/?[^>]+>/gi,
20698     
20699     /**
20700      * Strips all HTML tags to sort on text only
20701      * @param {Mixed} s The value being converted
20702      * @return {String} The comparison value
20703      */
20704     asText : function(s){
20705         return String(s).replace(this.stripTagsRE, "");
20706     },
20707     
20708     /**
20709      * Strips all HTML tags to sort on text only - Case insensitive
20710      * @param {Mixed} s The value being converted
20711      * @return {String} The comparison value
20712      */
20713     asUCText : function(s){
20714         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20715     },
20716     
20717     /**
20718      * Case insensitive string
20719      * @param {Mixed} s The value being converted
20720      * @return {String} The comparison value
20721      */
20722     asUCString : function(s) {
20723         return String(s).toUpperCase();
20724     },
20725     
20726     /**
20727      * Date sorting
20728      * @param {Mixed} s The value being converted
20729      * @return {Number} The comparison value
20730      */
20731     asDate : function(s) {
20732         if(!s){
20733             return 0;
20734         }
20735         if(s instanceof Date){
20736             return s.getTime();
20737         }
20738         return Date.parse(String(s));
20739     },
20740     
20741     /**
20742      * Float sorting
20743      * @param {Mixed} s The value being converted
20744      * @return {Float} The comparison value
20745      */
20746     asFloat : function(s) {
20747         var val = parseFloat(String(s).replace(/,/g, ""));
20748         if(isNaN(val)) val = 0;
20749         return val;
20750     },
20751     
20752     /**
20753      * Integer sorting
20754      * @param {Mixed} s The value being converted
20755      * @return {Number} The comparison value
20756      */
20757     asInt : function(s) {
20758         var val = parseInt(String(s).replace(/,/g, ""));
20759         if(isNaN(val)) val = 0;
20760         return val;
20761     }
20762 };/*
20763  * Based on:
20764  * Ext JS Library 1.1.1
20765  * Copyright(c) 2006-2007, Ext JS, LLC.
20766  *
20767  * Originally Released Under LGPL - original licence link has changed is not relivant.
20768  *
20769  * Fork - LGPL
20770  * <script type="text/javascript">
20771  */
20772
20773 /**
20774 * @class Roo.data.Record
20775  * Instances of this class encapsulate both record <em>definition</em> information, and record
20776  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20777  * to access Records cached in an {@link Roo.data.Store} object.<br>
20778  * <p>
20779  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20780  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20781  * objects.<br>
20782  * <p>
20783  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20784  * @constructor
20785  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20786  * {@link #create}. The parameters are the same.
20787  * @param {Array} data An associative Array of data values keyed by the field name.
20788  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20789  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20790  * not specified an integer id is generated.
20791  */
20792 Roo.data.Record = function(data, id){
20793     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20794     this.data = data;
20795 };
20796
20797 /**
20798  * Generate a constructor for a specific record layout.
20799  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20800  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20801  * Each field definition object may contain the following properties: <ul>
20802  * <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,
20803  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20804  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20805  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20806  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20807  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20808  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20809  * this may be omitted.</p></li>
20810  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20811  * <ul><li>auto (Default, implies no conversion)</li>
20812  * <li>string</li>
20813  * <li>int</li>
20814  * <li>float</li>
20815  * <li>boolean</li>
20816  * <li>date</li></ul></p></li>
20817  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20818  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20819  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20820  * by the Reader into an object that will be stored in the Record. It is passed the
20821  * following parameters:<ul>
20822  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20823  * </ul></p></li>
20824  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20825  * </ul>
20826  * <br>usage:<br><pre><code>
20827 var TopicRecord = Roo.data.Record.create(
20828     {name: 'title', mapping: 'topic_title'},
20829     {name: 'author', mapping: 'username'},
20830     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20831     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20832     {name: 'lastPoster', mapping: 'user2'},
20833     {name: 'excerpt', mapping: 'post_text'}
20834 );
20835
20836 var myNewRecord = new TopicRecord({
20837     title: 'Do my job please',
20838     author: 'noobie',
20839     totalPosts: 1,
20840     lastPost: new Date(),
20841     lastPoster: 'Animal',
20842     excerpt: 'No way dude!'
20843 });
20844 myStore.add(myNewRecord);
20845 </code></pre>
20846  * @method create
20847  * @static
20848  */
20849 Roo.data.Record.create = function(o){
20850     var f = function(){
20851         f.superclass.constructor.apply(this, arguments);
20852     };
20853     Roo.extend(f, Roo.data.Record);
20854     var p = f.prototype;
20855     p.fields = new Roo.util.MixedCollection(false, function(field){
20856         return field.name;
20857     });
20858     for(var i = 0, len = o.length; i < len; i++){
20859         p.fields.add(new Roo.data.Field(o[i]));
20860     }
20861     f.getField = function(name){
20862         return p.fields.get(name);  
20863     };
20864     return f;
20865 };
20866
20867 Roo.data.Record.AUTO_ID = 1000;
20868 Roo.data.Record.EDIT = 'edit';
20869 Roo.data.Record.REJECT = 'reject';
20870 Roo.data.Record.COMMIT = 'commit';
20871
20872 Roo.data.Record.prototype = {
20873     /**
20874      * Readonly flag - true if this record has been modified.
20875      * @type Boolean
20876      */
20877     dirty : false,
20878     editing : false,
20879     error: null,
20880     modified: null,
20881
20882     // private
20883     join : function(store){
20884         this.store = store;
20885     },
20886
20887     /**
20888      * Set the named field to the specified value.
20889      * @param {String} name The name of the field to set.
20890      * @param {Object} value The value to set the field to.
20891      */
20892     set : function(name, value){
20893         if(this.data[name] == value){
20894             return;
20895         }
20896         this.dirty = true;
20897         if(!this.modified){
20898             this.modified = {};
20899         }
20900         if(typeof this.modified[name] == 'undefined'){
20901             this.modified[name] = this.data[name];
20902         }
20903         this.data[name] = value;
20904         if(!this.editing && this.store){
20905             this.store.afterEdit(this);
20906         }       
20907     },
20908
20909     /**
20910      * Get the value of the named field.
20911      * @param {String} name The name of the field to get the value of.
20912      * @return {Object} The value of the field.
20913      */
20914     get : function(name){
20915         return this.data[name]; 
20916     },
20917
20918     // private
20919     beginEdit : function(){
20920         this.editing = true;
20921         this.modified = {}; 
20922     },
20923
20924     // private
20925     cancelEdit : function(){
20926         this.editing = false;
20927         delete this.modified;
20928     },
20929
20930     // private
20931     endEdit : function(){
20932         this.editing = false;
20933         if(this.dirty && this.store){
20934             this.store.afterEdit(this);
20935         }
20936     },
20937
20938     /**
20939      * Usually called by the {@link Roo.data.Store} which owns the Record.
20940      * Rejects all changes made to the Record since either creation, or the last commit operation.
20941      * Modified fields are reverted to their original values.
20942      * <p>
20943      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20944      * of reject operations.
20945      */
20946     reject : function(){
20947         var m = this.modified;
20948         for(var n in m){
20949             if(typeof m[n] != "function"){
20950                 this.data[n] = m[n];
20951             }
20952         }
20953         this.dirty = false;
20954         delete this.modified;
20955         this.editing = false;
20956         if(this.store){
20957             this.store.afterReject(this);
20958         }
20959     },
20960
20961     /**
20962      * Usually called by the {@link Roo.data.Store} which owns the Record.
20963      * Commits all changes made to the Record since either creation, or the last commit operation.
20964      * <p>
20965      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20966      * of commit operations.
20967      */
20968     commit : function(){
20969         this.dirty = false;
20970         delete this.modified;
20971         this.editing = false;
20972         if(this.store){
20973             this.store.afterCommit(this);
20974         }
20975     },
20976
20977     // private
20978     hasError : function(){
20979         return this.error != null;
20980     },
20981
20982     // private
20983     clearError : function(){
20984         this.error = null;
20985     },
20986
20987     /**
20988      * Creates a copy of this record.
20989      * @param {String} id (optional) A new record id if you don't want to use this record's id
20990      * @return {Record}
20991      */
20992     copy : function(newId) {
20993         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
20994     }
20995 };/*
20996  * Based on:
20997  * Ext JS Library 1.1.1
20998  * Copyright(c) 2006-2007, Ext JS, LLC.
20999  *
21000  * Originally Released Under LGPL - original licence link has changed is not relivant.
21001  *
21002  * Fork - LGPL
21003  * <script type="text/javascript">
21004  */
21005
21006
21007
21008 /**
21009  * @class Roo.data.Store
21010  * @extends Roo.util.Observable
21011  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21012  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21013  * <p>
21014  * 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
21015  * has no knowledge of the format of the data returned by the Proxy.<br>
21016  * <p>
21017  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21018  * instances from the data object. These records are cached and made available through accessor functions.
21019  * @constructor
21020  * Creates a new Store.
21021  * @param {Object} config A config object containing the objects needed for the Store to access data,
21022  * and read the data into Records.
21023  */
21024 Roo.data.Store = function(config){
21025     this.data = new Roo.util.MixedCollection(false);
21026     this.data.getKey = function(o){
21027         return o.id;
21028     };
21029     this.baseParams = {};
21030     // private
21031     this.paramNames = {
21032         "start" : "start",
21033         "limit" : "limit",
21034         "sort" : "sort",
21035         "dir" : "dir",
21036         "multisort" : "_multisort"
21037     };
21038
21039     if(config && config.data){
21040         this.inlineData = config.data;
21041         delete config.data;
21042     }
21043
21044     Roo.apply(this, config);
21045     
21046     if(this.reader){ // reader passed
21047         this.reader = Roo.factory(this.reader, Roo.data);
21048         this.reader.xmodule = this.xmodule || false;
21049         if(!this.recordType){
21050             this.recordType = this.reader.recordType;
21051         }
21052         if(this.reader.onMetaChange){
21053             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21054         }
21055     }
21056
21057     if(this.recordType){
21058         this.fields = this.recordType.prototype.fields;
21059     }
21060     this.modified = [];
21061
21062     this.addEvents({
21063         /**
21064          * @event datachanged
21065          * Fires when the data cache has changed, and a widget which is using this Store
21066          * as a Record cache should refresh its view.
21067          * @param {Store} this
21068          */
21069         datachanged : true,
21070         /**
21071          * @event metachange
21072          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21073          * @param {Store} this
21074          * @param {Object} meta The JSON metadata
21075          */
21076         metachange : true,
21077         /**
21078          * @event add
21079          * Fires when Records have been added to the Store
21080          * @param {Store} this
21081          * @param {Roo.data.Record[]} records The array of Records added
21082          * @param {Number} index The index at which the record(s) were added
21083          */
21084         add : true,
21085         /**
21086          * @event remove
21087          * Fires when a Record has been removed from the Store
21088          * @param {Store} this
21089          * @param {Roo.data.Record} record The Record that was removed
21090          * @param {Number} index The index at which the record was removed
21091          */
21092         remove : true,
21093         /**
21094          * @event update
21095          * Fires when a Record has been updated
21096          * @param {Store} this
21097          * @param {Roo.data.Record} record The Record that was updated
21098          * @param {String} operation The update operation being performed.  Value may be one of:
21099          * <pre><code>
21100  Roo.data.Record.EDIT
21101  Roo.data.Record.REJECT
21102  Roo.data.Record.COMMIT
21103          * </code></pre>
21104          */
21105         update : true,
21106         /**
21107          * @event clear
21108          * Fires when the data cache has been cleared.
21109          * @param {Store} this
21110          */
21111         clear : true,
21112         /**
21113          * @event beforeload
21114          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21115          * the load action will be canceled.
21116          * @param {Store} this
21117          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21118          */
21119         beforeload : true,
21120         /**
21121          * @event beforeloadadd
21122          * Fires after a new set of Records has been loaded.
21123          * @param {Store} this
21124          * @param {Roo.data.Record[]} records The Records that were loaded
21125          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21126          */
21127         beforeloadadd : true,
21128         /**
21129          * @event load
21130          * Fires after a new set of Records has been loaded, before they are added to the store.
21131          * @param {Store} this
21132          * @param {Roo.data.Record[]} records The Records that were loaded
21133          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21134          * @params {Object} return from reader
21135          */
21136         load : true,
21137         /**
21138          * @event loadexception
21139          * Fires if an exception occurs in the Proxy during loading.
21140          * Called with the signature of the Proxy's "loadexception" event.
21141          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21142          * 
21143          * @param {Proxy} 
21144          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21145          * @param {Object} load options 
21146          * @param {Object} jsonData from your request (normally this contains the Exception)
21147          */
21148         loadexception : true
21149     });
21150     
21151     if(this.proxy){
21152         this.proxy = Roo.factory(this.proxy, Roo.data);
21153         this.proxy.xmodule = this.xmodule || false;
21154         this.relayEvents(this.proxy,  ["loadexception"]);
21155     }
21156     this.sortToggle = {};
21157     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21158
21159     Roo.data.Store.superclass.constructor.call(this);
21160
21161     if(this.inlineData){
21162         this.loadData(this.inlineData);
21163         delete this.inlineData;
21164     }
21165 };
21166
21167 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21168      /**
21169     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21170     * without a remote query - used by combo/forms at present.
21171     */
21172     
21173     /**
21174     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21175     */
21176     /**
21177     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21178     */
21179     /**
21180     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21181     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21182     */
21183     /**
21184     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21185     * on any HTTP request
21186     */
21187     /**
21188     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21189     */
21190     /**
21191     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21192     */
21193     multiSort: false,
21194     /**
21195     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21196     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21197     */
21198     remoteSort : false,
21199
21200     /**
21201     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21202      * loaded or when a record is removed. (defaults to false).
21203     */
21204     pruneModifiedRecords : false,
21205
21206     // private
21207     lastOptions : null,
21208
21209     /**
21210      * Add Records to the Store and fires the add event.
21211      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21212      */
21213     add : function(records){
21214         records = [].concat(records);
21215         for(var i = 0, len = records.length; i < len; i++){
21216             records[i].join(this);
21217         }
21218         var index = this.data.length;
21219         this.data.addAll(records);
21220         this.fireEvent("add", this, records, index);
21221     },
21222
21223     /**
21224      * Remove a Record from the Store and fires the remove event.
21225      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21226      */
21227     remove : function(record){
21228         var index = this.data.indexOf(record);
21229         this.data.removeAt(index);
21230         if(this.pruneModifiedRecords){
21231             this.modified.remove(record);
21232         }
21233         this.fireEvent("remove", this, record, index);
21234     },
21235
21236     /**
21237      * Remove all Records from the Store and fires the clear event.
21238      */
21239     removeAll : function(){
21240         this.data.clear();
21241         if(this.pruneModifiedRecords){
21242             this.modified = [];
21243         }
21244         this.fireEvent("clear", this);
21245     },
21246
21247     /**
21248      * Inserts Records to the Store at the given index and fires the add event.
21249      * @param {Number} index The start index at which to insert the passed Records.
21250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21251      */
21252     insert : function(index, records){
21253         records = [].concat(records);
21254         for(var i = 0, len = records.length; i < len; i++){
21255             this.data.insert(index, records[i]);
21256             records[i].join(this);
21257         }
21258         this.fireEvent("add", this, records, index);
21259     },
21260
21261     /**
21262      * Get the index within the cache of the passed Record.
21263      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21264      * @return {Number} The index of the passed Record. Returns -1 if not found.
21265      */
21266     indexOf : function(record){
21267         return this.data.indexOf(record);
21268     },
21269
21270     /**
21271      * Get the index within the cache of the Record with the passed id.
21272      * @param {String} id The id of the Record to find.
21273      * @return {Number} The index of the Record. Returns -1 if not found.
21274      */
21275     indexOfId : function(id){
21276         return this.data.indexOfKey(id);
21277     },
21278
21279     /**
21280      * Get the Record with the specified id.
21281      * @param {String} id The id of the Record to find.
21282      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21283      */
21284     getById : function(id){
21285         return this.data.key(id);
21286     },
21287
21288     /**
21289      * Get the Record at the specified index.
21290      * @param {Number} index The index of the Record to find.
21291      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21292      */
21293     getAt : function(index){
21294         return this.data.itemAt(index);
21295     },
21296
21297     /**
21298      * Returns a range of Records between specified indices.
21299      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21300      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21301      * @return {Roo.data.Record[]} An array of Records
21302      */
21303     getRange : function(start, end){
21304         return this.data.getRange(start, end);
21305     },
21306
21307     // private
21308     storeOptions : function(o){
21309         o = Roo.apply({}, o);
21310         delete o.callback;
21311         delete o.scope;
21312         this.lastOptions = o;
21313     },
21314
21315     /**
21316      * Loads the Record cache from the configured Proxy using the configured Reader.
21317      * <p>
21318      * If using remote paging, then the first load call must specify the <em>start</em>
21319      * and <em>limit</em> properties in the options.params property to establish the initial
21320      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21321      * <p>
21322      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21323      * and this call will return before the new data has been loaded. Perform any post-processing
21324      * in a callback function, or in a "load" event handler.</strong>
21325      * <p>
21326      * @param {Object} options An object containing properties which control loading options:<ul>
21327      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21328      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21329      * passed the following arguments:<ul>
21330      * <li>r : Roo.data.Record[]</li>
21331      * <li>options: Options object from the load call</li>
21332      * <li>success: Boolean success indicator</li></ul></li>
21333      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21334      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21335      * </ul>
21336      */
21337     load : function(options){
21338         options = options || {};
21339         if(this.fireEvent("beforeload", this, options) !== false){
21340             this.storeOptions(options);
21341             var p = Roo.apply(options.params || {}, this.baseParams);
21342             // if meta was not loaded from remote source.. try requesting it.
21343             if (!this.reader.metaFromRemote) {
21344                 p._requestMeta = 1;
21345             }
21346             if(this.sortInfo && this.remoteSort){
21347                 var pn = this.paramNames;
21348                 p[pn["sort"]] = this.sortInfo.field;
21349                 p[pn["dir"]] = this.sortInfo.direction;
21350             }
21351             if (this.multiSort) {
21352                 var pn = this.paramNames;
21353                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21354             }
21355             
21356             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21357         }
21358     },
21359
21360     /**
21361      * Reloads the Record cache from the configured Proxy using the configured Reader and
21362      * the options from the last load operation performed.
21363      * @param {Object} options (optional) An object containing properties which may override the options
21364      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21365      * the most recently used options are reused).
21366      */
21367     reload : function(options){
21368         this.load(Roo.applyIf(options||{}, this.lastOptions));
21369     },
21370
21371     // private
21372     // Called as a callback by the Reader during a load operation.
21373     loadRecords : function(o, options, success){
21374         if(!o || success === false){
21375             if(success !== false){
21376                 this.fireEvent("load", this, [], options, o);
21377             }
21378             if(options.callback){
21379                 options.callback.call(options.scope || this, [], options, false);
21380             }
21381             return;
21382         }
21383         // if data returned failure - throw an exception.
21384         if (o.success === false) {
21385             // show a message if no listener is registered.
21386             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21387                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21388             }
21389             // loadmask wil be hooked into this..
21390             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21391             return;
21392         }
21393         var r = o.records, t = o.totalRecords || r.length;
21394         
21395         this.fireEvent("beforeloadadd", this, r, options, o);
21396         
21397         if(!options || options.add !== true){
21398             if(this.pruneModifiedRecords){
21399                 this.modified = [];
21400             }
21401             for(var i = 0, len = r.length; i < len; i++){
21402                 r[i].join(this);
21403             }
21404             if(this.snapshot){
21405                 this.data = this.snapshot;
21406                 delete this.snapshot;
21407             }
21408             this.data.clear();
21409             this.data.addAll(r);
21410             this.totalLength = t;
21411             this.applySort();
21412             this.fireEvent("datachanged", this);
21413         }else{
21414             this.totalLength = Math.max(t, this.data.length+r.length);
21415             this.add(r);
21416         }
21417         this.fireEvent("load", this, r, options, o);
21418         if(options.callback){
21419             options.callback.call(options.scope || this, r, options, true);
21420         }
21421     },
21422
21423
21424     /**
21425      * Loads data from a passed data block. A Reader which understands the format of the data
21426      * must have been configured in the constructor.
21427      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21428      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21429      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21430      */
21431     loadData : function(o, append){
21432         var r = this.reader.readRecords(o);
21433         this.loadRecords(r, {add: append}, true);
21434     },
21435
21436     /**
21437      * Gets the number of cached records.
21438      * <p>
21439      * <em>If using paging, this may not be the total size of the dataset. If the data object
21440      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21441      * the data set size</em>
21442      */
21443     getCount : function(){
21444         return this.data.length || 0;
21445     },
21446
21447     /**
21448      * Gets the total number of records in the dataset as returned by the server.
21449      * <p>
21450      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21451      * the dataset size</em>
21452      */
21453     getTotalCount : function(){
21454         return this.totalLength || 0;
21455     },
21456
21457     /**
21458      * Returns the sort state of the Store as an object with two properties:
21459      * <pre><code>
21460  field {String} The name of the field by which the Records are sorted
21461  direction {String} The sort order, "ASC" or "DESC"
21462      * </code></pre>
21463      */
21464     getSortState : function(){
21465         return this.sortInfo;
21466     },
21467
21468     // private
21469     applySort : function(){
21470         if(this.sortInfo && !this.remoteSort){
21471             var s = this.sortInfo, f = s.field;
21472             var st = this.fields.get(f).sortType;
21473             var fn = function(r1, r2){
21474                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21475                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21476             };
21477             this.data.sort(s.direction, fn);
21478             if(this.snapshot && this.snapshot != this.data){
21479                 this.snapshot.sort(s.direction, fn);
21480             }
21481         }
21482     },
21483
21484     /**
21485      * Sets the default sort column and order to be used by the next load operation.
21486      * @param {String} fieldName The name of the field to sort by.
21487      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21488      */
21489     setDefaultSort : function(field, dir){
21490         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21491     },
21492
21493     /**
21494      * Sort the Records.
21495      * If remote sorting is used, the sort is performed on the server, and the cache is
21496      * reloaded. If local sorting is used, the cache is sorted internally.
21497      * @param {String} fieldName The name of the field to sort by.
21498      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21499      */
21500     sort : function(fieldName, dir){
21501         var f = this.fields.get(fieldName);
21502         if(!dir){
21503             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21504             
21505             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21506                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21507             }else{
21508                 dir = f.sortDir;
21509             }
21510         }
21511         this.sortToggle[f.name] = dir;
21512         this.sortInfo = {field: f.name, direction: dir};
21513         if(!this.remoteSort){
21514             this.applySort();
21515             this.fireEvent("datachanged", this);
21516         }else{
21517             this.load(this.lastOptions);
21518         }
21519     },
21520
21521     /**
21522      * Calls the specified function for each of the Records in the cache.
21523      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21524      * Returning <em>false</em> aborts and exits the iteration.
21525      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21526      */
21527     each : function(fn, scope){
21528         this.data.each(fn, scope);
21529     },
21530
21531     /**
21532      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21533      * (e.g., during paging).
21534      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21535      */
21536     getModifiedRecords : function(){
21537         return this.modified;
21538     },
21539
21540     // private
21541     createFilterFn : function(property, value, anyMatch){
21542         if(!value.exec){ // not a regex
21543             value = String(value);
21544             if(value.length == 0){
21545                 return false;
21546             }
21547             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21548         }
21549         return function(r){
21550             return value.test(r.data[property]);
21551         };
21552     },
21553
21554     /**
21555      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21556      * @param {String} property A field on your records
21557      * @param {Number} start The record index to start at (defaults to 0)
21558      * @param {Number} end The last record index to include (defaults to length - 1)
21559      * @return {Number} The sum
21560      */
21561     sum : function(property, start, end){
21562         var rs = this.data.items, v = 0;
21563         start = start || 0;
21564         end = (end || end === 0) ? end : rs.length-1;
21565
21566         for(var i = start; i <= end; i++){
21567             v += (rs[i].data[property] || 0);
21568         }
21569         return v;
21570     },
21571
21572     /**
21573      * Filter the records by a specified property.
21574      * @param {String} field A field on your records
21575      * @param {String/RegExp} value Either a string that the field
21576      * should start with or a RegExp to test against the field
21577      * @param {Boolean} anyMatch True to match any part not just the beginning
21578      */
21579     filter : function(property, value, anyMatch){
21580         var fn = this.createFilterFn(property, value, anyMatch);
21581         return fn ? this.filterBy(fn) : this.clearFilter();
21582     },
21583
21584     /**
21585      * Filter by a function. The specified function will be called with each
21586      * record in this data source. If the function returns true the record is included,
21587      * otherwise it is filtered.
21588      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21589      * @param {Object} scope (optional) The scope of the function (defaults to this)
21590      */
21591     filterBy : function(fn, scope){
21592         this.snapshot = this.snapshot || this.data;
21593         this.data = this.queryBy(fn, scope||this);
21594         this.fireEvent("datachanged", this);
21595     },
21596
21597     /**
21598      * Query the records by a specified property.
21599      * @param {String} field A field on your records
21600      * @param {String/RegExp} value Either a string that the field
21601      * should start with or a RegExp to test against the field
21602      * @param {Boolean} anyMatch True to match any part not just the beginning
21603      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21604      */
21605     query : function(property, value, anyMatch){
21606         var fn = this.createFilterFn(property, value, anyMatch);
21607         return fn ? this.queryBy(fn) : this.data.clone();
21608     },
21609
21610     /**
21611      * Query by a function. The specified function will be called with each
21612      * record in this data source. If the function returns true the record is included
21613      * in the results.
21614      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21615      * @param {Object} scope (optional) The scope of the function (defaults to this)
21616       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21617      **/
21618     queryBy : function(fn, scope){
21619         var data = this.snapshot || this.data;
21620         return data.filterBy(fn, scope||this);
21621     },
21622
21623     /**
21624      * Collects unique values for a particular dataIndex from this store.
21625      * @param {String} dataIndex The property to collect
21626      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21627      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21628      * @return {Array} An array of the unique values
21629      **/
21630     collect : function(dataIndex, allowNull, bypassFilter){
21631         var d = (bypassFilter === true && this.snapshot) ?
21632                 this.snapshot.items : this.data.items;
21633         var v, sv, r = [], l = {};
21634         for(var i = 0, len = d.length; i < len; i++){
21635             v = d[i].data[dataIndex];
21636             sv = String(v);
21637             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21638                 l[sv] = true;
21639                 r[r.length] = v;
21640             }
21641         }
21642         return r;
21643     },
21644
21645     /**
21646      * Revert to a view of the Record cache with no filtering applied.
21647      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21648      */
21649     clearFilter : function(suppressEvent){
21650         if(this.snapshot && this.snapshot != this.data){
21651             this.data = this.snapshot;
21652             delete this.snapshot;
21653             if(suppressEvent !== true){
21654                 this.fireEvent("datachanged", this);
21655             }
21656         }
21657     },
21658
21659     // private
21660     afterEdit : function(record){
21661         if(this.modified.indexOf(record) == -1){
21662             this.modified.push(record);
21663         }
21664         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21665     },
21666     
21667     // private
21668     afterReject : function(record){
21669         this.modified.remove(record);
21670         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21671     },
21672
21673     // private
21674     afterCommit : function(record){
21675         this.modified.remove(record);
21676         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21677     },
21678
21679     /**
21680      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21681      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21682      */
21683     commitChanges : function(){
21684         var m = this.modified.slice(0);
21685         this.modified = [];
21686         for(var i = 0, len = m.length; i < len; i++){
21687             m[i].commit();
21688         }
21689     },
21690
21691     /**
21692      * Cancel outstanding changes on all changed records.
21693      */
21694     rejectChanges : function(){
21695         var m = this.modified.slice(0);
21696         this.modified = [];
21697         for(var i = 0, len = m.length; i < len; i++){
21698             m[i].reject();
21699         }
21700     },
21701
21702     onMetaChange : function(meta, rtype, o){
21703         this.recordType = rtype;
21704         this.fields = rtype.prototype.fields;
21705         delete this.snapshot;
21706         this.sortInfo = meta.sortInfo || this.sortInfo;
21707         this.modified = [];
21708         this.fireEvent('metachange', this, this.reader.meta);
21709     }
21710 });/*
21711  * Based on:
21712  * Ext JS Library 1.1.1
21713  * Copyright(c) 2006-2007, Ext JS, LLC.
21714  *
21715  * Originally Released Under LGPL - original licence link has changed is not relivant.
21716  *
21717  * Fork - LGPL
21718  * <script type="text/javascript">
21719  */
21720
21721 /**
21722  * @class Roo.data.SimpleStore
21723  * @extends Roo.data.Store
21724  * Small helper class to make creating Stores from Array data easier.
21725  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21726  * @cfg {Array} fields An array of field definition objects, or field name strings.
21727  * @cfg {Array} data The multi-dimensional array of data
21728  * @constructor
21729  * @param {Object} config
21730  */
21731 Roo.data.SimpleStore = function(config){
21732     Roo.data.SimpleStore.superclass.constructor.call(this, {
21733         isLocal : true,
21734         reader: new Roo.data.ArrayReader({
21735                 id: config.id
21736             },
21737             Roo.data.Record.create(config.fields)
21738         ),
21739         proxy : new Roo.data.MemoryProxy(config.data)
21740     });
21741     this.load();
21742 };
21743 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21744  * Based on:
21745  * Ext JS Library 1.1.1
21746  * Copyright(c) 2006-2007, Ext JS, LLC.
21747  *
21748  * Originally Released Under LGPL - original licence link has changed is not relivant.
21749  *
21750  * Fork - LGPL
21751  * <script type="text/javascript">
21752  */
21753
21754 /**
21755 /**
21756  * @extends Roo.data.Store
21757  * @class Roo.data.JsonStore
21758  * Small helper class to make creating Stores for JSON data easier. <br/>
21759 <pre><code>
21760 var store = new Roo.data.JsonStore({
21761     url: 'get-images.php',
21762     root: 'images',
21763     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21764 });
21765 </code></pre>
21766  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21767  * JsonReader and HttpProxy (unless inline data is provided).</b>
21768  * @cfg {Array} fields An array of field definition objects, or field name strings.
21769  * @constructor
21770  * @param {Object} config
21771  */
21772 Roo.data.JsonStore = function(c){
21773     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21774         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21775         reader: new Roo.data.JsonReader(c, c.fields)
21776     }));
21777 };
21778 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21779  * Based on:
21780  * Ext JS Library 1.1.1
21781  * Copyright(c) 2006-2007, Ext JS, LLC.
21782  *
21783  * Originally Released Under LGPL - original licence link has changed is not relivant.
21784  *
21785  * Fork - LGPL
21786  * <script type="text/javascript">
21787  */
21788
21789  
21790 Roo.data.Field = function(config){
21791     if(typeof config == "string"){
21792         config = {name: config};
21793     }
21794     Roo.apply(this, config);
21795     
21796     if(!this.type){
21797         this.type = "auto";
21798     }
21799     
21800     var st = Roo.data.SortTypes;
21801     // named sortTypes are supported, here we look them up
21802     if(typeof this.sortType == "string"){
21803         this.sortType = st[this.sortType];
21804     }
21805     
21806     // set default sortType for strings and dates
21807     if(!this.sortType){
21808         switch(this.type){
21809             case "string":
21810                 this.sortType = st.asUCString;
21811                 break;
21812             case "date":
21813                 this.sortType = st.asDate;
21814                 break;
21815             default:
21816                 this.sortType = st.none;
21817         }
21818     }
21819
21820     // define once
21821     var stripRe = /[\$,%]/g;
21822
21823     // prebuilt conversion function for this field, instead of
21824     // switching every time we're reading a value
21825     if(!this.convert){
21826         var cv, dateFormat = this.dateFormat;
21827         switch(this.type){
21828             case "":
21829             case "auto":
21830             case undefined:
21831                 cv = function(v){ return v; };
21832                 break;
21833             case "string":
21834                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21835                 break;
21836             case "int":
21837                 cv = function(v){
21838                     return v !== undefined && v !== null && v !== '' ?
21839                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21840                     };
21841                 break;
21842             case "float":
21843                 cv = function(v){
21844                     return v !== undefined && v !== null && v !== '' ?
21845                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21846                     };
21847                 break;
21848             case "bool":
21849             case "boolean":
21850                 cv = function(v){ return v === true || v === "true" || v == 1; };
21851                 break;
21852             case "date":
21853                 cv = function(v){
21854                     if(!v){
21855                         return '';
21856                     }
21857                     if(v instanceof Date){
21858                         return v;
21859                     }
21860                     if(dateFormat){
21861                         if(dateFormat == "timestamp"){
21862                             return new Date(v*1000);
21863                         }
21864                         return Date.parseDate(v, dateFormat);
21865                     }
21866                     var parsed = Date.parse(v);
21867                     return parsed ? new Date(parsed) : null;
21868                 };
21869              break;
21870             
21871         }
21872         this.convert = cv;
21873     }
21874 };
21875
21876 Roo.data.Field.prototype = {
21877     dateFormat: null,
21878     defaultValue: "",
21879     mapping: null,
21880     sortType : null,
21881     sortDir : "ASC"
21882 };/*
21883  * Based on:
21884  * Ext JS Library 1.1.1
21885  * Copyright(c) 2006-2007, Ext JS, LLC.
21886  *
21887  * Originally Released Under LGPL - original licence link has changed is not relivant.
21888  *
21889  * Fork - LGPL
21890  * <script type="text/javascript">
21891  */
21892  
21893 // Base class for reading structured data from a data source.  This class is intended to be
21894 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21895
21896 /**
21897  * @class Roo.data.DataReader
21898  * Base class for reading structured data from a data source.  This class is intended to be
21899  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21900  */
21901
21902 Roo.data.DataReader = function(meta, recordType){
21903     
21904     this.meta = meta;
21905     
21906     this.recordType = recordType instanceof Array ? 
21907         Roo.data.Record.create(recordType) : recordType;
21908 };
21909
21910 Roo.data.DataReader.prototype = {
21911      /**
21912      * Create an empty record
21913      * @param {Object} data (optional) - overlay some values
21914      * @return {Roo.data.Record} record created.
21915      */
21916     newRow :  function(d) {
21917         var da =  {};
21918         this.recordType.prototype.fields.each(function(c) {
21919             switch( c.type) {
21920                 case 'int' : da[c.name] = 0; break;
21921                 case 'date' : da[c.name] = new Date(); break;
21922                 case 'float' : da[c.name] = 0.0; break;
21923                 case 'boolean' : da[c.name] = false; break;
21924                 default : da[c.name] = ""; break;
21925             }
21926             
21927         });
21928         return new this.recordType(Roo.apply(da, d));
21929     }
21930     
21931 };/*
21932  * Based on:
21933  * Ext JS Library 1.1.1
21934  * Copyright(c) 2006-2007, Ext JS, LLC.
21935  *
21936  * Originally Released Under LGPL - original licence link has changed is not relivant.
21937  *
21938  * Fork - LGPL
21939  * <script type="text/javascript">
21940  */
21941
21942 /**
21943  * @class Roo.data.DataProxy
21944  * @extends Roo.data.Observable
21945  * This class is an abstract base class for implementations which provide retrieval of
21946  * unformatted data objects.<br>
21947  * <p>
21948  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21949  * (of the appropriate type which knows how to parse the data object) to provide a block of
21950  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21951  * <p>
21952  * Custom implementations must implement the load method as described in
21953  * {@link Roo.data.HttpProxy#load}.
21954  */
21955 Roo.data.DataProxy = function(){
21956     this.addEvents({
21957         /**
21958          * @event beforeload
21959          * Fires before a network request is made to retrieve a data object.
21960          * @param {Object} This DataProxy object.
21961          * @param {Object} params The params parameter to the load function.
21962          */
21963         beforeload : true,
21964         /**
21965          * @event load
21966          * Fires before the load method's callback is called.
21967          * @param {Object} This DataProxy object.
21968          * @param {Object} o The data object.
21969          * @param {Object} arg The callback argument object passed to the load function.
21970          */
21971         load : true,
21972         /**
21973          * @event loadexception
21974          * Fires if an Exception occurs during data retrieval.
21975          * @param {Object} This DataProxy object.
21976          * @param {Object} o The data object.
21977          * @param {Object} arg The callback argument object passed to the load function.
21978          * @param {Object} e The Exception.
21979          */
21980         loadexception : true
21981     });
21982     Roo.data.DataProxy.superclass.constructor.call(this);
21983 };
21984
21985 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
21986
21987     /**
21988      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
21989      */
21990 /*
21991  * Based on:
21992  * Ext JS Library 1.1.1
21993  * Copyright(c) 2006-2007, Ext JS, LLC.
21994  *
21995  * Originally Released Under LGPL - original licence link has changed is not relivant.
21996  *
21997  * Fork - LGPL
21998  * <script type="text/javascript">
21999  */
22000 /**
22001  * @class Roo.data.MemoryProxy
22002  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22003  * to the Reader when its load method is called.
22004  * @constructor
22005  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22006  */
22007 Roo.data.MemoryProxy = function(data){
22008     if (data.data) {
22009         data = data.data;
22010     }
22011     Roo.data.MemoryProxy.superclass.constructor.call(this);
22012     this.data = data;
22013 };
22014
22015 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22016     /**
22017      * Load data from the requested source (in this case an in-memory
22018      * data object passed to the constructor), read the data object into
22019      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22020      * process that block using the passed callback.
22021      * @param {Object} params This parameter is not used by the MemoryProxy class.
22022      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22023      * object into a block of Roo.data.Records.
22024      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22025      * The function must be passed <ul>
22026      * <li>The Record block object</li>
22027      * <li>The "arg" argument from the load function</li>
22028      * <li>A boolean success indicator</li>
22029      * </ul>
22030      * @param {Object} scope The scope in which to call the callback
22031      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22032      */
22033     load : function(params, reader, callback, scope, arg){
22034         params = params || {};
22035         var result;
22036         try {
22037             result = reader.readRecords(this.data);
22038         }catch(e){
22039             this.fireEvent("loadexception", this, arg, null, e);
22040             callback.call(scope, null, arg, false);
22041             return;
22042         }
22043         callback.call(scope, result, arg, true);
22044     },
22045     
22046     // private
22047     update : function(params, records){
22048         
22049     }
22050 });/*
22051  * Based on:
22052  * Ext JS Library 1.1.1
22053  * Copyright(c) 2006-2007, Ext JS, LLC.
22054  *
22055  * Originally Released Under LGPL - original licence link has changed is not relivant.
22056  *
22057  * Fork - LGPL
22058  * <script type="text/javascript">
22059  */
22060 /**
22061  * @class Roo.data.HttpProxy
22062  * @extends Roo.data.DataProxy
22063  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22064  * configured to reference a certain URL.<br><br>
22065  * <p>
22066  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22067  * from which the running page was served.<br><br>
22068  * <p>
22069  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22070  * <p>
22071  * Be aware that to enable the browser to parse an XML document, the server must set
22072  * the Content-Type header in the HTTP response to "text/xml".
22073  * @constructor
22074  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22075  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22076  * will be used to make the request.
22077  */
22078 Roo.data.HttpProxy = function(conn){
22079     Roo.data.HttpProxy.superclass.constructor.call(this);
22080     // is conn a conn config or a real conn?
22081     this.conn = conn;
22082     this.useAjax = !conn || !conn.events;
22083   
22084 };
22085
22086 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22087     // thse are take from connection...
22088     
22089     /**
22090      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22091      */
22092     /**
22093      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22094      * extra parameters to each request made by this object. (defaults to undefined)
22095      */
22096     /**
22097      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22098      *  to each request made by this object. (defaults to undefined)
22099      */
22100     /**
22101      * @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)
22102      */
22103     /**
22104      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22105      */
22106      /**
22107      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22108      * @type Boolean
22109      */
22110   
22111
22112     /**
22113      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22114      * @type Boolean
22115      */
22116     /**
22117      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22118      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22119      * a finer-grained basis than the DataProxy events.
22120      */
22121     getConnection : function(){
22122         return this.useAjax ? Roo.Ajax : this.conn;
22123     },
22124
22125     /**
22126      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22127      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22128      * process that block using the passed callback.
22129      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22130      * for the request to the remote server.
22131      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22132      * object into a block of Roo.data.Records.
22133      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22134      * The function must be passed <ul>
22135      * <li>The Record block object</li>
22136      * <li>The "arg" argument from the load function</li>
22137      * <li>A boolean success indicator</li>
22138      * </ul>
22139      * @param {Object} scope The scope in which to call the callback
22140      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22141      */
22142     load : function(params, reader, callback, scope, arg){
22143         if(this.fireEvent("beforeload", this, params) !== false){
22144             var  o = {
22145                 params : params || {},
22146                 request: {
22147                     callback : callback,
22148                     scope : scope,
22149                     arg : arg
22150                 },
22151                 reader: reader,
22152                 callback : this.loadResponse,
22153                 scope: this
22154             };
22155             if(this.useAjax){
22156                 Roo.applyIf(o, this.conn);
22157                 if(this.activeRequest){
22158                     Roo.Ajax.abort(this.activeRequest);
22159                 }
22160                 this.activeRequest = Roo.Ajax.request(o);
22161             }else{
22162                 this.conn.request(o);
22163             }
22164         }else{
22165             callback.call(scope||this, null, arg, false);
22166         }
22167     },
22168
22169     // private
22170     loadResponse : function(o, success, response){
22171         delete this.activeRequest;
22172         if(!success){
22173             this.fireEvent("loadexception", this, o, response);
22174             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22175             return;
22176         }
22177         var result;
22178         try {
22179             result = o.reader.read(response);
22180         }catch(e){
22181             this.fireEvent("loadexception", this, o, response, e);
22182             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22183             return;
22184         }
22185         
22186         this.fireEvent("load", this, o, o.request.arg);
22187         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22188     },
22189
22190     // private
22191     update : function(dataSet){
22192
22193     },
22194
22195     // private
22196     updateResponse : function(dataSet){
22197
22198     }
22199 });/*
22200  * Based on:
22201  * Ext JS Library 1.1.1
22202  * Copyright(c) 2006-2007, Ext JS, LLC.
22203  *
22204  * Originally Released Under LGPL - original licence link has changed is not relivant.
22205  *
22206  * Fork - LGPL
22207  * <script type="text/javascript">
22208  */
22209
22210 /**
22211  * @class Roo.data.ScriptTagProxy
22212  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22213  * other than the originating domain of the running page.<br><br>
22214  * <p>
22215  * <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
22216  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22217  * <p>
22218  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22219  * source code that is used as the source inside a &lt;script> tag.<br><br>
22220  * <p>
22221  * In order for the browser to process the returned data, the server must wrap the data object
22222  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22223  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22224  * depending on whether the callback name was passed:
22225  * <p>
22226  * <pre><code>
22227 boolean scriptTag = false;
22228 String cb = request.getParameter("callback");
22229 if (cb != null) {
22230     scriptTag = true;
22231     response.setContentType("text/javascript");
22232 } else {
22233     response.setContentType("application/x-json");
22234 }
22235 Writer out = response.getWriter();
22236 if (scriptTag) {
22237     out.write(cb + "(");
22238 }
22239 out.print(dataBlock.toJsonString());
22240 if (scriptTag) {
22241     out.write(");");
22242 }
22243 </pre></code>
22244  *
22245  * @constructor
22246  * @param {Object} config A configuration object.
22247  */
22248 Roo.data.ScriptTagProxy = function(config){
22249     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22250     Roo.apply(this, config);
22251     this.head = document.getElementsByTagName("head")[0];
22252 };
22253
22254 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22255
22256 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22257     /**
22258      * @cfg {String} url The URL from which to request the data object.
22259      */
22260     /**
22261      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22262      */
22263     timeout : 30000,
22264     /**
22265      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22266      * the server the name of the callback function set up by the load call to process the returned data object.
22267      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22268      * javascript output which calls this named function passing the data object as its only parameter.
22269      */
22270     callbackParam : "callback",
22271     /**
22272      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22273      * name to the request.
22274      */
22275     nocache : true,
22276
22277     /**
22278      * Load data from the configured URL, read the data object into
22279      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22280      * process that block using the passed callback.
22281      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22282      * for the request to the remote server.
22283      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22284      * object into a block of Roo.data.Records.
22285      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22286      * The function must be passed <ul>
22287      * <li>The Record block object</li>
22288      * <li>The "arg" argument from the load function</li>
22289      * <li>A boolean success indicator</li>
22290      * </ul>
22291      * @param {Object} scope The scope in which to call the callback
22292      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22293      */
22294     load : function(params, reader, callback, scope, arg){
22295         if(this.fireEvent("beforeload", this, params) !== false){
22296
22297             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22298
22299             var url = this.url;
22300             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22301             if(this.nocache){
22302                 url += "&_dc=" + (new Date().getTime());
22303             }
22304             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22305             var trans = {
22306                 id : transId,
22307                 cb : "stcCallback"+transId,
22308                 scriptId : "stcScript"+transId,
22309                 params : params,
22310                 arg : arg,
22311                 url : url,
22312                 callback : callback,
22313                 scope : scope,
22314                 reader : reader
22315             };
22316             var conn = this;
22317
22318             window[trans.cb] = function(o){
22319                 conn.handleResponse(o, trans);
22320             };
22321
22322             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22323
22324             if(this.autoAbort !== false){
22325                 this.abort();
22326             }
22327
22328             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22329
22330             var script = document.createElement("script");
22331             script.setAttribute("src", url);
22332             script.setAttribute("type", "text/javascript");
22333             script.setAttribute("id", trans.scriptId);
22334             this.head.appendChild(script);
22335
22336             this.trans = trans;
22337         }else{
22338             callback.call(scope||this, null, arg, false);
22339         }
22340     },
22341
22342     // private
22343     isLoading : function(){
22344         return this.trans ? true : false;
22345     },
22346
22347     /**
22348      * Abort the current server request.
22349      */
22350     abort : function(){
22351         if(this.isLoading()){
22352             this.destroyTrans(this.trans);
22353         }
22354     },
22355
22356     // private
22357     destroyTrans : function(trans, isLoaded){
22358         this.head.removeChild(document.getElementById(trans.scriptId));
22359         clearTimeout(trans.timeoutId);
22360         if(isLoaded){
22361             window[trans.cb] = undefined;
22362             try{
22363                 delete window[trans.cb];
22364             }catch(e){}
22365         }else{
22366             // if hasn't been loaded, wait for load to remove it to prevent script error
22367             window[trans.cb] = function(){
22368                 window[trans.cb] = undefined;
22369                 try{
22370                     delete window[trans.cb];
22371                 }catch(e){}
22372             };
22373         }
22374     },
22375
22376     // private
22377     handleResponse : function(o, trans){
22378         this.trans = false;
22379         this.destroyTrans(trans, true);
22380         var result;
22381         try {
22382             result = trans.reader.readRecords(o);
22383         }catch(e){
22384             this.fireEvent("loadexception", this, o, trans.arg, e);
22385             trans.callback.call(trans.scope||window, null, trans.arg, false);
22386             return;
22387         }
22388         this.fireEvent("load", this, o, trans.arg);
22389         trans.callback.call(trans.scope||window, result, trans.arg, true);
22390     },
22391
22392     // private
22393     handleFailure : function(trans){
22394         this.trans = false;
22395         this.destroyTrans(trans, false);
22396         this.fireEvent("loadexception", this, null, trans.arg);
22397         trans.callback.call(trans.scope||window, null, trans.arg, false);
22398     }
22399 });/*
22400  * Based on:
22401  * Ext JS Library 1.1.1
22402  * Copyright(c) 2006-2007, Ext JS, LLC.
22403  *
22404  * Originally Released Under LGPL - original licence link has changed is not relivant.
22405  *
22406  * Fork - LGPL
22407  * <script type="text/javascript">
22408  */
22409
22410 /**
22411  * @class Roo.data.JsonReader
22412  * @extends Roo.data.DataReader
22413  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22414  * based on mappings in a provided Roo.data.Record constructor.
22415  * 
22416  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22417  * in the reply previously. 
22418  * 
22419  * <p>
22420  * Example code:
22421  * <pre><code>
22422 var RecordDef = Roo.data.Record.create([
22423     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22424     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22425 ]);
22426 var myReader = new Roo.data.JsonReader({
22427     totalProperty: "results",    // The property which contains the total dataset size (optional)
22428     root: "rows",                // The property which contains an Array of row objects
22429     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22430 }, RecordDef);
22431 </code></pre>
22432  * <p>
22433  * This would consume a JSON file like this:
22434  * <pre><code>
22435 { 'results': 2, 'rows': [
22436     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22437     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22438 }
22439 </code></pre>
22440  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22441  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22442  * paged from the remote server.
22443  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22444  * @cfg {String} root name of the property which contains the Array of row objects.
22445  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22446  * @constructor
22447  * Create a new JsonReader
22448  * @param {Object} meta Metadata configuration options
22449  * @param {Object} recordType Either an Array of field definition objects,
22450  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22451  */
22452 Roo.data.JsonReader = function(meta, recordType){
22453     
22454     meta = meta || {};
22455     // set some defaults:
22456     Roo.applyIf(meta, {
22457         totalProperty: 'total',
22458         successProperty : 'success',
22459         root : 'data',
22460         id : 'id'
22461     });
22462     
22463     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22464 };
22465 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22466     
22467     /**
22468      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22469      * Used by Store query builder to append _requestMeta to params.
22470      * 
22471      */
22472     metaFromRemote : false,
22473     /**
22474      * This method is only used by a DataProxy which has retrieved data from a remote server.
22475      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22476      * @return {Object} data A data block which is used by an Roo.data.Store object as
22477      * a cache of Roo.data.Records.
22478      */
22479     read : function(response){
22480         var json = response.responseText;
22481        
22482         var o = /* eval:var:o */ eval("("+json+")");
22483         if(!o) {
22484             throw {message: "JsonReader.read: Json object not found"};
22485         }
22486         
22487         if(o.metaData){
22488             
22489             delete this.ef;
22490             this.metaFromRemote = true;
22491             this.meta = o.metaData;
22492             this.recordType = Roo.data.Record.create(o.metaData.fields);
22493             this.onMetaChange(this.meta, this.recordType, o);
22494         }
22495         return this.readRecords(o);
22496     },
22497
22498     // private function a store will implement
22499     onMetaChange : function(meta, recordType, o){
22500
22501     },
22502
22503     /**
22504          * @ignore
22505          */
22506     simpleAccess: function(obj, subsc) {
22507         return obj[subsc];
22508     },
22509
22510         /**
22511          * @ignore
22512          */
22513     getJsonAccessor: function(){
22514         var re = /[\[\.]/;
22515         return function(expr) {
22516             try {
22517                 return(re.test(expr))
22518                     ? new Function("obj", "return obj." + expr)
22519                     : function(obj){
22520                         return obj[expr];
22521                     };
22522             } catch(e){}
22523             return Roo.emptyFn;
22524         };
22525     }(),
22526
22527     /**
22528      * Create a data block containing Roo.data.Records from an XML document.
22529      * @param {Object} o An object which contains an Array of row objects in the property specified
22530      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22531      * which contains the total size of the dataset.
22532      * @return {Object} data A data block which is used by an Roo.data.Store object as
22533      * a cache of Roo.data.Records.
22534      */
22535     readRecords : function(o){
22536         /**
22537          * After any data loads, the raw JSON data is available for further custom processing.
22538          * @type Object
22539          */
22540         this.o = o;
22541         var s = this.meta, Record = this.recordType,
22542             f = Record.prototype.fields, fi = f.items, fl = f.length;
22543
22544 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22545         if (!this.ef) {
22546             if(s.totalProperty) {
22547                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22548                 }
22549                 if(s.successProperty) {
22550                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22551                 }
22552                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22553                 if (s.id) {
22554                         var g = this.getJsonAccessor(s.id);
22555                         this.getId = function(rec) {
22556                                 var r = g(rec);
22557                                 return (r === undefined || r === "") ? null : r;
22558                         };
22559                 } else {
22560                         this.getId = function(){return null;};
22561                 }
22562             this.ef = [];
22563             for(var jj = 0; jj < fl; jj++){
22564                 f = fi[jj];
22565                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22566                 this.ef[jj] = this.getJsonAccessor(map);
22567             }
22568         }
22569
22570         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22571         if(s.totalProperty){
22572             var vt = parseInt(this.getTotal(o), 10);
22573             if(!isNaN(vt)){
22574                 totalRecords = vt;
22575             }
22576         }
22577         if(s.successProperty){
22578             var vs = this.getSuccess(o);
22579             if(vs === false || vs === 'false'){
22580                 success = false;
22581             }
22582         }
22583         var records = [];
22584             for(var i = 0; i < c; i++){
22585                     var n = root[i];
22586                 var values = {};
22587                 var id = this.getId(n);
22588                 for(var j = 0; j < fl; j++){
22589                     f = fi[j];
22590                 var v = this.ef[j](n);
22591                 if (!f.convert) {
22592                     Roo.log('missing convert for ' + f.name);
22593                     Roo.log(f);
22594                     continue;
22595                 }
22596                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22597                 }
22598                 var record = new Record(values, id);
22599                 record.json = n;
22600                 records[i] = record;
22601             }
22602             return {
22603             raw : o,
22604                 success : success,
22605                 records : records,
22606                 totalRecords : totalRecords
22607             };
22608     }
22609 });/*
22610  * Based on:
22611  * Ext JS Library 1.1.1
22612  * Copyright(c) 2006-2007, Ext JS, LLC.
22613  *
22614  * Originally Released Under LGPL - original licence link has changed is not relivant.
22615  *
22616  * Fork - LGPL
22617  * <script type="text/javascript">
22618  */
22619
22620 /**
22621  * @class Roo.data.XmlReader
22622  * @extends Roo.data.DataReader
22623  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22624  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22625  * <p>
22626  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22627  * header in the HTTP response must be set to "text/xml".</em>
22628  * <p>
22629  * Example code:
22630  * <pre><code>
22631 var RecordDef = Roo.data.Record.create([
22632    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22633    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22634 ]);
22635 var myReader = new Roo.data.XmlReader({
22636    totalRecords: "results", // The element which contains the total dataset size (optional)
22637    record: "row",           // The repeated element which contains row information
22638    id: "id"                 // The element within the row that provides an ID for the record (optional)
22639 }, RecordDef);
22640 </code></pre>
22641  * <p>
22642  * This would consume an XML file like this:
22643  * <pre><code>
22644 &lt;?xml?>
22645 &lt;dataset>
22646  &lt;results>2&lt;/results>
22647  &lt;row>
22648    &lt;id>1&lt;/id>
22649    &lt;name>Bill&lt;/name>
22650    &lt;occupation>Gardener&lt;/occupation>
22651  &lt;/row>
22652  &lt;row>
22653    &lt;id>2&lt;/id>
22654    &lt;name>Ben&lt;/name>
22655    &lt;occupation>Horticulturalist&lt;/occupation>
22656  &lt;/row>
22657 &lt;/dataset>
22658 </code></pre>
22659  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22660  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22661  * paged from the remote server.
22662  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22663  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22664  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22665  * a record identifier value.
22666  * @constructor
22667  * Create a new XmlReader
22668  * @param {Object} meta Metadata configuration options
22669  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22670  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22671  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22672  */
22673 Roo.data.XmlReader = function(meta, recordType){
22674     meta = meta || {};
22675     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22676 };
22677 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22678     /**
22679      * This method is only used by a DataProxy which has retrieved data from a remote server.
22680          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22681          * to contain a method called 'responseXML' that returns an XML document object.
22682      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22683      * a cache of Roo.data.Records.
22684      */
22685     read : function(response){
22686         var doc = response.responseXML;
22687         if(!doc) {
22688             throw {message: "XmlReader.read: XML Document not available"};
22689         }
22690         return this.readRecords(doc);
22691     },
22692
22693     /**
22694      * Create a data block containing Roo.data.Records from an XML document.
22695          * @param {Object} doc A parsed XML document.
22696      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22697      * a cache of Roo.data.Records.
22698      */
22699     readRecords : function(doc){
22700         /**
22701          * After any data loads/reads, the raw XML Document is available for further custom processing.
22702          * @type XMLDocument
22703          */
22704         this.xmlData = doc;
22705         var root = doc.documentElement || doc;
22706         var q = Roo.DomQuery;
22707         var recordType = this.recordType, fields = recordType.prototype.fields;
22708         var sid = this.meta.id;
22709         var totalRecords = 0, success = true;
22710         if(this.meta.totalRecords){
22711             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22712         }
22713         
22714         if(this.meta.success){
22715             var sv = q.selectValue(this.meta.success, root, true);
22716             success = sv !== false && sv !== 'false';
22717         }
22718         var records = [];
22719         var ns = q.select(this.meta.record, root);
22720         for(var i = 0, len = ns.length; i < len; i++) {
22721                 var n = ns[i];
22722                 var values = {};
22723                 var id = sid ? q.selectValue(sid, n) : undefined;
22724                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22725                     var f = fields.items[j];
22726                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22727                     v = f.convert(v);
22728                     values[f.name] = v;
22729                 }
22730                 var record = new recordType(values, id);
22731                 record.node = n;
22732                 records[records.length] = record;
22733             }
22734
22735             return {
22736                 success : success,
22737                 records : records,
22738                 totalRecords : totalRecords || records.length
22739             };
22740     }
22741 });/*
22742  * Based on:
22743  * Ext JS Library 1.1.1
22744  * Copyright(c) 2006-2007, Ext JS, LLC.
22745  *
22746  * Originally Released Under LGPL - original licence link has changed is not relivant.
22747  *
22748  * Fork - LGPL
22749  * <script type="text/javascript">
22750  */
22751
22752 /**
22753  * @class Roo.data.ArrayReader
22754  * @extends Roo.data.DataReader
22755  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22756  * Each element of that Array represents a row of data fields. The
22757  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22758  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22759  * <p>
22760  * Example code:.
22761  * <pre><code>
22762 var RecordDef = Roo.data.Record.create([
22763     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22764     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22765 ]);
22766 var myReader = new Roo.data.ArrayReader({
22767     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22768 }, RecordDef);
22769 </code></pre>
22770  * <p>
22771  * This would consume an Array like this:
22772  * <pre><code>
22773 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22774   </code></pre>
22775  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22776  * @constructor
22777  * Create a new JsonReader
22778  * @param {Object} meta Metadata configuration options.
22779  * @param {Object} recordType Either an Array of field definition objects
22780  * as specified to {@link Roo.data.Record#create},
22781  * or an {@link Roo.data.Record} object
22782  * created using {@link Roo.data.Record#create}.
22783  */
22784 Roo.data.ArrayReader = function(meta, recordType){
22785     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22786 };
22787
22788 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22789     /**
22790      * Create a data block containing Roo.data.Records from an XML document.
22791      * @param {Object} o An Array of row objects which represents the dataset.
22792      * @return {Object} data A data block which is used by an Roo.data.Store object as
22793      * a cache of Roo.data.Records.
22794      */
22795     readRecords : function(o){
22796         var sid = this.meta ? this.meta.id : null;
22797         var recordType = this.recordType, fields = recordType.prototype.fields;
22798         var records = [];
22799         var root = o;
22800             for(var i = 0; i < root.length; i++){
22801                     var n = root[i];
22802                 var values = {};
22803                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22804                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22805                 var f = fields.items[j];
22806                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22807                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22808                 v = f.convert(v);
22809                 values[f.name] = v;
22810             }
22811                 var record = new recordType(values, id);
22812                 record.json = n;
22813                 records[records.length] = record;
22814             }
22815             return {
22816                 records : records,
22817                 totalRecords : records.length
22818             };
22819     }
22820 });/*
22821  * Based on:
22822  * Ext JS Library 1.1.1
22823  * Copyright(c) 2006-2007, Ext JS, LLC.
22824  *
22825  * Originally Released Under LGPL - original licence link has changed is not relivant.
22826  *
22827  * Fork - LGPL
22828  * <script type="text/javascript">
22829  */
22830
22831
22832 /**
22833  * @class Roo.data.Tree
22834  * @extends Roo.util.Observable
22835  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22836  * in the tree have most standard DOM functionality.
22837  * @constructor
22838  * @param {Node} root (optional) The root node
22839  */
22840 Roo.data.Tree = function(root){
22841    this.nodeHash = {};
22842    /**
22843     * The root node for this tree
22844     * @type Node
22845     */
22846    this.root = null;
22847    if(root){
22848        this.setRootNode(root);
22849    }
22850    this.addEvents({
22851        /**
22852         * @event append
22853         * Fires when a new child node is appended to a node in this tree.
22854         * @param {Tree} tree The owner tree
22855         * @param {Node} parent The parent node
22856         * @param {Node} node The newly appended node
22857         * @param {Number} index The index of the newly appended node
22858         */
22859        "append" : true,
22860        /**
22861         * @event remove
22862         * Fires when a child node is removed from a node in this tree.
22863         * @param {Tree} tree The owner tree
22864         * @param {Node} parent The parent node
22865         * @param {Node} node The child node removed
22866         */
22867        "remove" : true,
22868        /**
22869         * @event move
22870         * Fires when a node is moved to a new location in the tree
22871         * @param {Tree} tree The owner tree
22872         * @param {Node} node The node moved
22873         * @param {Node} oldParent The old parent of this node
22874         * @param {Node} newParent The new parent of this node
22875         * @param {Number} index The index it was moved to
22876         */
22877        "move" : true,
22878        /**
22879         * @event insert
22880         * Fires when a new child node is inserted in a node in this tree.
22881         * @param {Tree} tree The owner tree
22882         * @param {Node} parent The parent node
22883         * @param {Node} node The child node inserted
22884         * @param {Node} refNode The child node the node was inserted before
22885         */
22886        "insert" : true,
22887        /**
22888         * @event beforeappend
22889         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22890         * @param {Tree} tree The owner tree
22891         * @param {Node} parent The parent node
22892         * @param {Node} node The child node to be appended
22893         */
22894        "beforeappend" : true,
22895        /**
22896         * @event beforeremove
22897         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22898         * @param {Tree} tree The owner tree
22899         * @param {Node} parent The parent node
22900         * @param {Node} node The child node to be removed
22901         */
22902        "beforeremove" : true,
22903        /**
22904         * @event beforemove
22905         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22906         * @param {Tree} tree The owner tree
22907         * @param {Node} node The node being moved
22908         * @param {Node} oldParent The parent of the node
22909         * @param {Node} newParent The new parent the node is moving to
22910         * @param {Number} index The index it is being moved to
22911         */
22912        "beforemove" : true,
22913        /**
22914         * @event beforeinsert
22915         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22916         * @param {Tree} tree The owner tree
22917         * @param {Node} parent The parent node
22918         * @param {Node} node The child node to be inserted
22919         * @param {Node} refNode The child node the node is being inserted before
22920         */
22921        "beforeinsert" : true
22922    });
22923
22924     Roo.data.Tree.superclass.constructor.call(this);
22925 };
22926
22927 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22928     pathSeparator: "/",
22929
22930     proxyNodeEvent : function(){
22931         return this.fireEvent.apply(this, arguments);
22932     },
22933
22934     /**
22935      * Returns the root node for this tree.
22936      * @return {Node}
22937      */
22938     getRootNode : function(){
22939         return this.root;
22940     },
22941
22942     /**
22943      * Sets the root node for this tree.
22944      * @param {Node} node
22945      * @return {Node}
22946      */
22947     setRootNode : function(node){
22948         this.root = node;
22949         node.ownerTree = this;
22950         node.isRoot = true;
22951         this.registerNode(node);
22952         return node;
22953     },
22954
22955     /**
22956      * Gets a node in this tree by its id.
22957      * @param {String} id
22958      * @return {Node}
22959      */
22960     getNodeById : function(id){
22961         return this.nodeHash[id];
22962     },
22963
22964     registerNode : function(node){
22965         this.nodeHash[node.id] = node;
22966     },
22967
22968     unregisterNode : function(node){
22969         delete this.nodeHash[node.id];
22970     },
22971
22972     toString : function(){
22973         return "[Tree"+(this.id?" "+this.id:"")+"]";
22974     }
22975 });
22976
22977 /**
22978  * @class Roo.data.Node
22979  * @extends Roo.util.Observable
22980  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
22981  * @cfg {String} id The id for this node. If one is not specified, one is generated.
22982  * @constructor
22983  * @param {Object} attributes The attributes/config for the node
22984  */
22985 Roo.data.Node = function(attributes){
22986     /**
22987      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
22988      * @type {Object}
22989      */
22990     this.attributes = attributes || {};
22991     this.leaf = this.attributes.leaf;
22992     /**
22993      * The node id. @type String
22994      */
22995     this.id = this.attributes.id;
22996     if(!this.id){
22997         this.id = Roo.id(null, "ynode-");
22998         this.attributes.id = this.id;
22999     }
23000      
23001     
23002     /**
23003      * All child nodes of this node. @type Array
23004      */
23005     this.childNodes = [];
23006     if(!this.childNodes.indexOf){ // indexOf is a must
23007         this.childNodes.indexOf = function(o){
23008             for(var i = 0, len = this.length; i < len; i++){
23009                 if(this[i] == o) {
23010                     return i;
23011                 }
23012             }
23013             return -1;
23014         };
23015     }
23016     /**
23017      * The parent node for this node. @type Node
23018      */
23019     this.parentNode = null;
23020     /**
23021      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23022      */
23023     this.firstChild = null;
23024     /**
23025      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23026      */
23027     this.lastChild = null;
23028     /**
23029      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23030      */
23031     this.previousSibling = null;
23032     /**
23033      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23034      */
23035     this.nextSibling = null;
23036
23037     this.addEvents({
23038        /**
23039         * @event append
23040         * Fires when a new child node is appended
23041         * @param {Tree} tree The owner tree
23042         * @param {Node} this This node
23043         * @param {Node} node The newly appended node
23044         * @param {Number} index The index of the newly appended node
23045         */
23046        "append" : true,
23047        /**
23048         * @event remove
23049         * Fires when a child node is removed
23050         * @param {Tree} tree The owner tree
23051         * @param {Node} this This node
23052         * @param {Node} node The removed node
23053         */
23054        "remove" : true,
23055        /**
23056         * @event move
23057         * Fires when this node is moved to a new location in the tree
23058         * @param {Tree} tree The owner tree
23059         * @param {Node} this This node
23060         * @param {Node} oldParent The old parent of this node
23061         * @param {Node} newParent The new parent of this node
23062         * @param {Number} index The index it was moved to
23063         */
23064        "move" : true,
23065        /**
23066         * @event insert
23067         * Fires when a new child node is inserted.
23068         * @param {Tree} tree The owner tree
23069         * @param {Node} this This node
23070         * @param {Node} node The child node inserted
23071         * @param {Node} refNode The child node the node was inserted before
23072         */
23073        "insert" : true,
23074        /**
23075         * @event beforeappend
23076         * Fires before a new child is appended, return false to cancel the append.
23077         * @param {Tree} tree The owner tree
23078         * @param {Node} this This node
23079         * @param {Node} node The child node to be appended
23080         */
23081        "beforeappend" : true,
23082        /**
23083         * @event beforeremove
23084         * Fires before a child is removed, return false to cancel the remove.
23085         * @param {Tree} tree The owner tree
23086         * @param {Node} this This node
23087         * @param {Node} node The child node to be removed
23088         */
23089        "beforeremove" : true,
23090        /**
23091         * @event beforemove
23092         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23093         * @param {Tree} tree The owner tree
23094         * @param {Node} this This node
23095         * @param {Node} oldParent The parent of this node
23096         * @param {Node} newParent The new parent this node is moving to
23097         * @param {Number} index The index it is being moved to
23098         */
23099        "beforemove" : true,
23100        /**
23101         * @event beforeinsert
23102         * Fires before a new child is inserted, return false to cancel the insert.
23103         * @param {Tree} tree The owner tree
23104         * @param {Node} this This node
23105         * @param {Node} node The child node to be inserted
23106         * @param {Node} refNode The child node the node is being inserted before
23107         */
23108        "beforeinsert" : true
23109    });
23110     this.listeners = this.attributes.listeners;
23111     Roo.data.Node.superclass.constructor.call(this);
23112 };
23113
23114 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23115     fireEvent : function(evtName){
23116         // first do standard event for this node
23117         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23118             return false;
23119         }
23120         // then bubble it up to the tree if the event wasn't cancelled
23121         var ot = this.getOwnerTree();
23122         if(ot){
23123             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23124                 return false;
23125             }
23126         }
23127         return true;
23128     },
23129
23130     /**
23131      * Returns true if this node is a leaf
23132      * @return {Boolean}
23133      */
23134     isLeaf : function(){
23135         return this.leaf === true;
23136     },
23137
23138     // private
23139     setFirstChild : function(node){
23140         this.firstChild = node;
23141     },
23142
23143     //private
23144     setLastChild : function(node){
23145         this.lastChild = node;
23146     },
23147
23148
23149     /**
23150      * Returns true if this node is the last child of its parent
23151      * @return {Boolean}
23152      */
23153     isLast : function(){
23154        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23155     },
23156
23157     /**
23158      * Returns true if this node is the first child of its parent
23159      * @return {Boolean}
23160      */
23161     isFirst : function(){
23162        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23163     },
23164
23165     hasChildNodes : function(){
23166         return !this.isLeaf() && this.childNodes.length > 0;
23167     },
23168
23169     /**
23170      * Insert node(s) as the last child node of this node.
23171      * @param {Node/Array} node The node or Array of nodes to append
23172      * @return {Node} The appended node if single append, or null if an array was passed
23173      */
23174     appendChild : function(node){
23175         var multi = false;
23176         if(node instanceof Array){
23177             multi = node;
23178         }else if(arguments.length > 1){
23179             multi = arguments;
23180         }
23181         // if passed an array or multiple args do them one by one
23182         if(multi){
23183             for(var i = 0, len = multi.length; i < len; i++) {
23184                 this.appendChild(multi[i]);
23185             }
23186         }else{
23187             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23188                 return false;
23189             }
23190             var index = this.childNodes.length;
23191             var oldParent = node.parentNode;
23192             // it's a move, make sure we move it cleanly
23193             if(oldParent){
23194                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23195                     return false;
23196                 }
23197                 oldParent.removeChild(node);
23198             }
23199             index = this.childNodes.length;
23200             if(index == 0){
23201                 this.setFirstChild(node);
23202             }
23203             this.childNodes.push(node);
23204             node.parentNode = this;
23205             var ps = this.childNodes[index-1];
23206             if(ps){
23207                 node.previousSibling = ps;
23208                 ps.nextSibling = node;
23209             }else{
23210                 node.previousSibling = null;
23211             }
23212             node.nextSibling = null;
23213             this.setLastChild(node);
23214             node.setOwnerTree(this.getOwnerTree());
23215             this.fireEvent("append", this.ownerTree, this, node, index);
23216             if(oldParent){
23217                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23218             }
23219             return node;
23220         }
23221     },
23222
23223     /**
23224      * Removes a child node from this node.
23225      * @param {Node} node The node to remove
23226      * @return {Node} The removed node
23227      */
23228     removeChild : function(node){
23229         var index = this.childNodes.indexOf(node);
23230         if(index == -1){
23231             return false;
23232         }
23233         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23234             return false;
23235         }
23236
23237         // remove it from childNodes collection
23238         this.childNodes.splice(index, 1);
23239
23240         // update siblings
23241         if(node.previousSibling){
23242             node.previousSibling.nextSibling = node.nextSibling;
23243         }
23244         if(node.nextSibling){
23245             node.nextSibling.previousSibling = node.previousSibling;
23246         }
23247
23248         // update child refs
23249         if(this.firstChild == node){
23250             this.setFirstChild(node.nextSibling);
23251         }
23252         if(this.lastChild == node){
23253             this.setLastChild(node.previousSibling);
23254         }
23255
23256         node.setOwnerTree(null);
23257         // clear any references from the node
23258         node.parentNode = null;
23259         node.previousSibling = null;
23260         node.nextSibling = null;
23261         this.fireEvent("remove", this.ownerTree, this, node);
23262         return node;
23263     },
23264
23265     /**
23266      * Inserts the first node before the second node in this nodes childNodes collection.
23267      * @param {Node} node The node to insert
23268      * @param {Node} refNode The node to insert before (if null the node is appended)
23269      * @return {Node} The inserted node
23270      */
23271     insertBefore : function(node, refNode){
23272         if(!refNode){ // like standard Dom, refNode can be null for append
23273             return this.appendChild(node);
23274         }
23275         // nothing to do
23276         if(node == refNode){
23277             return false;
23278         }
23279
23280         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23281             return false;
23282         }
23283         var index = this.childNodes.indexOf(refNode);
23284         var oldParent = node.parentNode;
23285         var refIndex = index;
23286
23287         // when moving internally, indexes will change after remove
23288         if(oldParent == this && this.childNodes.indexOf(node) < index){
23289             refIndex--;
23290         }
23291
23292         // it's a move, make sure we move it cleanly
23293         if(oldParent){
23294             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23295                 return false;
23296             }
23297             oldParent.removeChild(node);
23298         }
23299         if(refIndex == 0){
23300             this.setFirstChild(node);
23301         }
23302         this.childNodes.splice(refIndex, 0, node);
23303         node.parentNode = this;
23304         var ps = this.childNodes[refIndex-1];
23305         if(ps){
23306             node.previousSibling = ps;
23307             ps.nextSibling = node;
23308         }else{
23309             node.previousSibling = null;
23310         }
23311         node.nextSibling = refNode;
23312         refNode.previousSibling = node;
23313         node.setOwnerTree(this.getOwnerTree());
23314         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23315         if(oldParent){
23316             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23317         }
23318         return node;
23319     },
23320
23321     /**
23322      * Returns the child node at the specified index.
23323      * @param {Number} index
23324      * @return {Node}
23325      */
23326     item : function(index){
23327         return this.childNodes[index];
23328     },
23329
23330     /**
23331      * Replaces one child node in this node with another.
23332      * @param {Node} newChild The replacement node
23333      * @param {Node} oldChild The node to replace
23334      * @return {Node} The replaced node
23335      */
23336     replaceChild : function(newChild, oldChild){
23337         this.insertBefore(newChild, oldChild);
23338         this.removeChild(oldChild);
23339         return oldChild;
23340     },
23341
23342     /**
23343      * Returns the index of a child node
23344      * @param {Node} node
23345      * @return {Number} The index of the node or -1 if it was not found
23346      */
23347     indexOf : function(child){
23348         return this.childNodes.indexOf(child);
23349     },
23350
23351     /**
23352      * Returns the tree this node is in.
23353      * @return {Tree}
23354      */
23355     getOwnerTree : function(){
23356         // if it doesn't have one, look for one
23357         if(!this.ownerTree){
23358             var p = this;
23359             while(p){
23360                 if(p.ownerTree){
23361                     this.ownerTree = p.ownerTree;
23362                     break;
23363                 }
23364                 p = p.parentNode;
23365             }
23366         }
23367         return this.ownerTree;
23368     },
23369
23370     /**
23371      * Returns depth of this node (the root node has a depth of 0)
23372      * @return {Number}
23373      */
23374     getDepth : function(){
23375         var depth = 0;
23376         var p = this;
23377         while(p.parentNode){
23378             ++depth;
23379             p = p.parentNode;
23380         }
23381         return depth;
23382     },
23383
23384     // private
23385     setOwnerTree : function(tree){
23386         // if it's move, we need to update everyone
23387         if(tree != this.ownerTree){
23388             if(this.ownerTree){
23389                 this.ownerTree.unregisterNode(this);
23390             }
23391             this.ownerTree = tree;
23392             var cs = this.childNodes;
23393             for(var i = 0, len = cs.length; i < len; i++) {
23394                 cs[i].setOwnerTree(tree);
23395             }
23396             if(tree){
23397                 tree.registerNode(this);
23398             }
23399         }
23400     },
23401
23402     /**
23403      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23404      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23405      * @return {String} The path
23406      */
23407     getPath : function(attr){
23408         attr = attr || "id";
23409         var p = this.parentNode;
23410         var b = [this.attributes[attr]];
23411         while(p){
23412             b.unshift(p.attributes[attr]);
23413             p = p.parentNode;
23414         }
23415         var sep = this.getOwnerTree().pathSeparator;
23416         return sep + b.join(sep);
23417     },
23418
23419     /**
23420      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23421      * function call will be the scope provided or the current node. The arguments to the function
23422      * will be the args provided or the current node. If the function returns false at any point,
23423      * the bubble is stopped.
23424      * @param {Function} fn The function to call
23425      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23426      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23427      */
23428     bubble : function(fn, scope, args){
23429         var p = this;
23430         while(p){
23431             if(fn.call(scope || p, args || p) === false){
23432                 break;
23433             }
23434             p = p.parentNode;
23435         }
23436     },
23437
23438     /**
23439      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23440      * function call will be the scope provided or the current node. The arguments to the function
23441      * will be the args provided or the current node. If the function returns false at any point,
23442      * the cascade is stopped on that branch.
23443      * @param {Function} fn The function to call
23444      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23445      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23446      */
23447     cascade : function(fn, scope, args){
23448         if(fn.call(scope || this, args || this) !== false){
23449             var cs = this.childNodes;
23450             for(var i = 0, len = cs.length; i < len; i++) {
23451                 cs[i].cascade(fn, scope, args);
23452             }
23453         }
23454     },
23455
23456     /**
23457      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23458      * function call will be the scope provided or the current node. The arguments to the function
23459      * will be the args provided or the current node. If the function returns false at any point,
23460      * the iteration stops.
23461      * @param {Function} fn The function to call
23462      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23463      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23464      */
23465     eachChild : function(fn, scope, args){
23466         var cs = this.childNodes;
23467         for(var i = 0, len = cs.length; i < len; i++) {
23468                 if(fn.call(scope || this, args || cs[i]) === false){
23469                     break;
23470                 }
23471         }
23472     },
23473
23474     /**
23475      * Finds the first child that has the attribute with the specified value.
23476      * @param {String} attribute The attribute name
23477      * @param {Mixed} value The value to search for
23478      * @return {Node} The found child or null if none was found
23479      */
23480     findChild : function(attribute, value){
23481         var cs = this.childNodes;
23482         for(var i = 0, len = cs.length; i < len; i++) {
23483                 if(cs[i].attributes[attribute] == value){
23484                     return cs[i];
23485                 }
23486         }
23487         return null;
23488     },
23489
23490     /**
23491      * Finds the first child by a custom function. The child matches if the function passed
23492      * returns true.
23493      * @param {Function} fn
23494      * @param {Object} scope (optional)
23495      * @return {Node} The found child or null if none was found
23496      */
23497     findChildBy : function(fn, scope){
23498         var cs = this.childNodes;
23499         for(var i = 0, len = cs.length; i < len; i++) {
23500                 if(fn.call(scope||cs[i], cs[i]) === true){
23501                     return cs[i];
23502                 }
23503         }
23504         return null;
23505     },
23506
23507     /**
23508      * Sorts this nodes children using the supplied sort function
23509      * @param {Function} fn
23510      * @param {Object} scope (optional)
23511      */
23512     sort : function(fn, scope){
23513         var cs = this.childNodes;
23514         var len = cs.length;
23515         if(len > 0){
23516             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23517             cs.sort(sortFn);
23518             for(var i = 0; i < len; i++){
23519                 var n = cs[i];
23520                 n.previousSibling = cs[i-1];
23521                 n.nextSibling = cs[i+1];
23522                 if(i == 0){
23523                     this.setFirstChild(n);
23524                 }
23525                 if(i == len-1){
23526                     this.setLastChild(n);
23527                 }
23528             }
23529         }
23530     },
23531
23532     /**
23533      * Returns true if this node is an ancestor (at any point) of the passed node.
23534      * @param {Node} node
23535      * @return {Boolean}
23536      */
23537     contains : function(node){
23538         return node.isAncestor(this);
23539     },
23540
23541     /**
23542      * Returns true if the passed node is an ancestor (at any point) of this node.
23543      * @param {Node} node
23544      * @return {Boolean}
23545      */
23546     isAncestor : function(node){
23547         var p = this.parentNode;
23548         while(p){
23549             if(p == node){
23550                 return true;
23551             }
23552             p = p.parentNode;
23553         }
23554         return false;
23555     },
23556
23557     toString : function(){
23558         return "[Node"+(this.id?" "+this.id:"")+"]";
23559     }
23560 });/*
23561  * Based on:
23562  * Ext JS Library 1.1.1
23563  * Copyright(c) 2006-2007, Ext JS, LLC.
23564  *
23565  * Originally Released Under LGPL - original licence link has changed is not relivant.
23566  *
23567  * Fork - LGPL
23568  * <script type="text/javascript">
23569  */
23570  (function(){ 
23571 /**
23572  * @class Roo.Layer
23573  * @extends Roo.Element
23574  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23575  * automatic maintaining of shadow/shim positions.
23576  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23577  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23578  * you can pass a string with a CSS class name. False turns off the shadow.
23579  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23580  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23581  * @cfg {String} cls CSS class to add to the element
23582  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23583  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23584  * @constructor
23585  * @param {Object} config An object with config options.
23586  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23587  */
23588
23589 Roo.Layer = function(config, existingEl){
23590     config = config || {};
23591     var dh = Roo.DomHelper;
23592     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23593     if(existingEl){
23594         this.dom = Roo.getDom(existingEl);
23595     }
23596     if(!this.dom){
23597         var o = config.dh || {tag: "div", cls: "x-layer"};
23598         this.dom = dh.append(pel, o);
23599     }
23600     if(config.cls){
23601         this.addClass(config.cls);
23602     }
23603     this.constrain = config.constrain !== false;
23604     this.visibilityMode = Roo.Element.VISIBILITY;
23605     if(config.id){
23606         this.id = this.dom.id = config.id;
23607     }else{
23608         this.id = Roo.id(this.dom);
23609     }
23610     this.zindex = config.zindex || this.getZIndex();
23611     this.position("absolute", this.zindex);
23612     if(config.shadow){
23613         this.shadowOffset = config.shadowOffset || 4;
23614         this.shadow = new Roo.Shadow({
23615             offset : this.shadowOffset,
23616             mode : config.shadow
23617         });
23618     }else{
23619         this.shadowOffset = 0;
23620     }
23621     this.useShim = config.shim !== false && Roo.useShims;
23622     this.useDisplay = config.useDisplay;
23623     this.hide();
23624 };
23625
23626 var supr = Roo.Element.prototype;
23627
23628 // shims are shared among layer to keep from having 100 iframes
23629 var shims = [];
23630
23631 Roo.extend(Roo.Layer, Roo.Element, {
23632
23633     getZIndex : function(){
23634         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23635     },
23636
23637     getShim : function(){
23638         if(!this.useShim){
23639             return null;
23640         }
23641         if(this.shim){
23642             return this.shim;
23643         }
23644         var shim = shims.shift();
23645         if(!shim){
23646             shim = this.createShim();
23647             shim.enableDisplayMode('block');
23648             shim.dom.style.display = 'none';
23649             shim.dom.style.visibility = 'visible';
23650         }
23651         var pn = this.dom.parentNode;
23652         if(shim.dom.parentNode != pn){
23653             pn.insertBefore(shim.dom, this.dom);
23654         }
23655         shim.setStyle('z-index', this.getZIndex()-2);
23656         this.shim = shim;
23657         return shim;
23658     },
23659
23660     hideShim : function(){
23661         if(this.shim){
23662             this.shim.setDisplayed(false);
23663             shims.push(this.shim);
23664             delete this.shim;
23665         }
23666     },
23667
23668     disableShadow : function(){
23669         if(this.shadow){
23670             this.shadowDisabled = true;
23671             this.shadow.hide();
23672             this.lastShadowOffset = this.shadowOffset;
23673             this.shadowOffset = 0;
23674         }
23675     },
23676
23677     enableShadow : function(show){
23678         if(this.shadow){
23679             this.shadowDisabled = false;
23680             this.shadowOffset = this.lastShadowOffset;
23681             delete this.lastShadowOffset;
23682             if(show){
23683                 this.sync(true);
23684             }
23685         }
23686     },
23687
23688     // private
23689     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23690     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23691     sync : function(doShow){
23692         var sw = this.shadow;
23693         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23694             var sh = this.getShim();
23695
23696             var w = this.getWidth(),
23697                 h = this.getHeight();
23698
23699             var l = this.getLeft(true),
23700                 t = this.getTop(true);
23701
23702             if(sw && !this.shadowDisabled){
23703                 if(doShow && !sw.isVisible()){
23704                     sw.show(this);
23705                 }else{
23706                     sw.realign(l, t, w, h);
23707                 }
23708                 if(sh){
23709                     if(doShow){
23710                        sh.show();
23711                     }
23712                     // fit the shim behind the shadow, so it is shimmed too
23713                     var a = sw.adjusts, s = sh.dom.style;
23714                     s.left = (Math.min(l, l+a.l))+"px";
23715                     s.top = (Math.min(t, t+a.t))+"px";
23716                     s.width = (w+a.w)+"px";
23717                     s.height = (h+a.h)+"px";
23718                 }
23719             }else if(sh){
23720                 if(doShow){
23721                    sh.show();
23722                 }
23723                 sh.setSize(w, h);
23724                 sh.setLeftTop(l, t);
23725             }
23726             
23727         }
23728     },
23729
23730     // private
23731     destroy : function(){
23732         this.hideShim();
23733         if(this.shadow){
23734             this.shadow.hide();
23735         }
23736         this.removeAllListeners();
23737         var pn = this.dom.parentNode;
23738         if(pn){
23739             pn.removeChild(this.dom);
23740         }
23741         Roo.Element.uncache(this.id);
23742     },
23743
23744     remove : function(){
23745         this.destroy();
23746     },
23747
23748     // private
23749     beginUpdate : function(){
23750         this.updating = true;
23751     },
23752
23753     // private
23754     endUpdate : function(){
23755         this.updating = false;
23756         this.sync(true);
23757     },
23758
23759     // private
23760     hideUnders : function(negOffset){
23761         if(this.shadow){
23762             this.shadow.hide();
23763         }
23764         this.hideShim();
23765     },
23766
23767     // private
23768     constrainXY : function(){
23769         if(this.constrain){
23770             var vw = Roo.lib.Dom.getViewWidth(),
23771                 vh = Roo.lib.Dom.getViewHeight();
23772             var s = Roo.get(document).getScroll();
23773
23774             var xy = this.getXY();
23775             var x = xy[0], y = xy[1];   
23776             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23777             // only move it if it needs it
23778             var moved = false;
23779             // first validate right/bottom
23780             if((x + w) > vw+s.left){
23781                 x = vw - w - this.shadowOffset;
23782                 moved = true;
23783             }
23784             if((y + h) > vh+s.top){
23785                 y = vh - h - this.shadowOffset;
23786                 moved = true;
23787             }
23788             // then make sure top/left isn't negative
23789             if(x < s.left){
23790                 x = s.left;
23791                 moved = true;
23792             }
23793             if(y < s.top){
23794                 y = s.top;
23795                 moved = true;
23796             }
23797             if(moved){
23798                 if(this.avoidY){
23799                     var ay = this.avoidY;
23800                     if(y <= ay && (y+h) >= ay){
23801                         y = ay-h-5;   
23802                     }
23803                 }
23804                 xy = [x, y];
23805                 this.storeXY(xy);
23806                 supr.setXY.call(this, xy);
23807                 this.sync();
23808             }
23809         }
23810     },
23811
23812     isVisible : function(){
23813         return this.visible;    
23814     },
23815
23816     // private
23817     showAction : function(){
23818         this.visible = true; // track visibility to prevent getStyle calls
23819         if(this.useDisplay === true){
23820             this.setDisplayed("");
23821         }else if(this.lastXY){
23822             supr.setXY.call(this, this.lastXY);
23823         }else if(this.lastLT){
23824             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23825         }
23826     },
23827
23828     // private
23829     hideAction : function(){
23830         this.visible = false;
23831         if(this.useDisplay === true){
23832             this.setDisplayed(false);
23833         }else{
23834             this.setLeftTop(-10000,-10000);
23835         }
23836     },
23837
23838     // overridden Element method
23839     setVisible : function(v, a, d, c, e){
23840         if(v){
23841             this.showAction();
23842         }
23843         if(a && v){
23844             var cb = function(){
23845                 this.sync(true);
23846                 if(c){
23847                     c();
23848                 }
23849             }.createDelegate(this);
23850             supr.setVisible.call(this, true, true, d, cb, e);
23851         }else{
23852             if(!v){
23853                 this.hideUnders(true);
23854             }
23855             var cb = c;
23856             if(a){
23857                 cb = function(){
23858                     this.hideAction();
23859                     if(c){
23860                         c();
23861                     }
23862                 }.createDelegate(this);
23863             }
23864             supr.setVisible.call(this, v, a, d, cb, e);
23865             if(v){
23866                 this.sync(true);
23867             }else if(!a){
23868                 this.hideAction();
23869             }
23870         }
23871     },
23872
23873     storeXY : function(xy){
23874         delete this.lastLT;
23875         this.lastXY = xy;
23876     },
23877
23878     storeLeftTop : function(left, top){
23879         delete this.lastXY;
23880         this.lastLT = [left, top];
23881     },
23882
23883     // private
23884     beforeFx : function(){
23885         this.beforeAction();
23886         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23887     },
23888
23889     // private
23890     afterFx : function(){
23891         Roo.Layer.superclass.afterFx.apply(this, arguments);
23892         this.sync(this.isVisible());
23893     },
23894
23895     // private
23896     beforeAction : function(){
23897         if(!this.updating && this.shadow){
23898             this.shadow.hide();
23899         }
23900     },
23901
23902     // overridden Element method
23903     setLeft : function(left){
23904         this.storeLeftTop(left, this.getTop(true));
23905         supr.setLeft.apply(this, arguments);
23906         this.sync();
23907     },
23908
23909     setTop : function(top){
23910         this.storeLeftTop(this.getLeft(true), top);
23911         supr.setTop.apply(this, arguments);
23912         this.sync();
23913     },
23914
23915     setLeftTop : function(left, top){
23916         this.storeLeftTop(left, top);
23917         supr.setLeftTop.apply(this, arguments);
23918         this.sync();
23919     },
23920
23921     setXY : function(xy, a, d, c, e){
23922         this.fixDisplay();
23923         this.beforeAction();
23924         this.storeXY(xy);
23925         var cb = this.createCB(c);
23926         supr.setXY.call(this, xy, a, d, cb, e);
23927         if(!a){
23928             cb();
23929         }
23930     },
23931
23932     // private
23933     createCB : function(c){
23934         var el = this;
23935         return function(){
23936             el.constrainXY();
23937             el.sync(true);
23938             if(c){
23939                 c();
23940             }
23941         };
23942     },
23943
23944     // overridden Element method
23945     setX : function(x, a, d, c, e){
23946         this.setXY([x, this.getY()], a, d, c, e);
23947     },
23948
23949     // overridden Element method
23950     setY : function(y, a, d, c, e){
23951         this.setXY([this.getX(), y], a, d, c, e);
23952     },
23953
23954     // overridden Element method
23955     setSize : function(w, h, a, d, c, e){
23956         this.beforeAction();
23957         var cb = this.createCB(c);
23958         supr.setSize.call(this, w, h, a, d, cb, e);
23959         if(!a){
23960             cb();
23961         }
23962     },
23963
23964     // overridden Element method
23965     setWidth : function(w, a, d, c, e){
23966         this.beforeAction();
23967         var cb = this.createCB(c);
23968         supr.setWidth.call(this, w, a, d, cb, e);
23969         if(!a){
23970             cb();
23971         }
23972     },
23973
23974     // overridden Element method
23975     setHeight : function(h, a, d, c, e){
23976         this.beforeAction();
23977         var cb = this.createCB(c);
23978         supr.setHeight.call(this, h, a, d, cb, e);
23979         if(!a){
23980             cb();
23981         }
23982     },
23983
23984     // overridden Element method
23985     setBounds : function(x, y, w, h, a, d, c, e){
23986         this.beforeAction();
23987         var cb = this.createCB(c);
23988         if(!a){
23989             this.storeXY([x, y]);
23990             supr.setXY.call(this, [x, y]);
23991             supr.setSize.call(this, w, h, a, d, cb, e);
23992             cb();
23993         }else{
23994             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23995         }
23996         return this;
23997     },
23998     
23999     /**
24000      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24001      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24002      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24003      * @param {Number} zindex The new z-index to set
24004      * @return {this} The Layer
24005      */
24006     setZIndex : function(zindex){
24007         this.zindex = zindex;
24008         this.setStyle("z-index", zindex + 2);
24009         if(this.shadow){
24010             this.shadow.setZIndex(zindex + 1);
24011         }
24012         if(this.shim){
24013             this.shim.setStyle("z-index", zindex);
24014         }
24015     }
24016 });
24017 })();/*
24018  * Based on:
24019  * Ext JS Library 1.1.1
24020  * Copyright(c) 2006-2007, Ext JS, LLC.
24021  *
24022  * Originally Released Under LGPL - original licence link has changed is not relivant.
24023  *
24024  * Fork - LGPL
24025  * <script type="text/javascript">
24026  */
24027
24028
24029 /**
24030  * @class Roo.Shadow
24031  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24032  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24033  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24034  * @constructor
24035  * Create a new Shadow
24036  * @param {Object} config The config object
24037  */
24038 Roo.Shadow = function(config){
24039     Roo.apply(this, config);
24040     if(typeof this.mode != "string"){
24041         this.mode = this.defaultMode;
24042     }
24043     var o = this.offset, a = {h: 0};
24044     var rad = Math.floor(this.offset/2);
24045     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24046         case "drop":
24047             a.w = 0;
24048             a.l = a.t = o;
24049             a.t -= 1;
24050             if(Roo.isIE){
24051                 a.l -= this.offset + rad;
24052                 a.t -= this.offset + rad;
24053                 a.w -= rad;
24054                 a.h -= rad;
24055                 a.t += 1;
24056             }
24057         break;
24058         case "sides":
24059             a.w = (o*2);
24060             a.l = -o;
24061             a.t = o-1;
24062             if(Roo.isIE){
24063                 a.l -= (this.offset - rad);
24064                 a.t -= this.offset + rad;
24065                 a.l += 1;
24066                 a.w -= (this.offset - rad)*2;
24067                 a.w -= rad + 1;
24068                 a.h -= 1;
24069             }
24070         break;
24071         case "frame":
24072             a.w = a.h = (o*2);
24073             a.l = a.t = -o;
24074             a.t += 1;
24075             a.h -= 2;
24076             if(Roo.isIE){
24077                 a.l -= (this.offset - rad);
24078                 a.t -= (this.offset - rad);
24079                 a.l += 1;
24080                 a.w -= (this.offset + rad + 1);
24081                 a.h -= (this.offset + rad);
24082                 a.h += 1;
24083             }
24084         break;
24085     };
24086
24087     this.adjusts = a;
24088 };
24089
24090 Roo.Shadow.prototype = {
24091     /**
24092      * @cfg {String} mode
24093      * The shadow display mode.  Supports the following options:<br />
24094      * sides: Shadow displays on both sides and bottom only<br />
24095      * frame: Shadow displays equally on all four sides<br />
24096      * drop: Traditional bottom-right drop shadow (default)
24097      */
24098     /**
24099      * @cfg {String} offset
24100      * The number of pixels to offset the shadow from the element (defaults to 4)
24101      */
24102     offset: 4,
24103
24104     // private
24105     defaultMode: "drop",
24106
24107     /**
24108      * Displays the shadow under the target element
24109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24110      */
24111     show : function(target){
24112         target = Roo.get(target);
24113         if(!this.el){
24114             this.el = Roo.Shadow.Pool.pull();
24115             if(this.el.dom.nextSibling != target.dom){
24116                 this.el.insertBefore(target);
24117             }
24118         }
24119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24120         if(Roo.isIE){
24121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24122         }
24123         this.realign(
24124             target.getLeft(true),
24125             target.getTop(true),
24126             target.getWidth(),
24127             target.getHeight()
24128         );
24129         this.el.dom.style.display = "block";
24130     },
24131
24132     /**
24133      * Returns true if the shadow is visible, else false
24134      */
24135     isVisible : function(){
24136         return this.el ? true : false;  
24137     },
24138
24139     /**
24140      * Direct alignment when values are already available. Show must be called at least once before
24141      * calling this method to ensure it is initialized.
24142      * @param {Number} left The target element left position
24143      * @param {Number} top The target element top position
24144      * @param {Number} width The target element width
24145      * @param {Number} height The target element height
24146      */
24147     realign : function(l, t, w, h){
24148         if(!this.el){
24149             return;
24150         }
24151         var a = this.adjusts, d = this.el.dom, s = d.style;
24152         var iea = 0;
24153         s.left = (l+a.l)+"px";
24154         s.top = (t+a.t)+"px";
24155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24156  
24157         if(s.width != sws || s.height != shs){
24158             s.width = sws;
24159             s.height = shs;
24160             if(!Roo.isIE){
24161                 var cn = d.childNodes;
24162                 var sww = Math.max(0, (sw-12))+"px";
24163                 cn[0].childNodes[1].style.width = sww;
24164                 cn[1].childNodes[1].style.width = sww;
24165                 cn[2].childNodes[1].style.width = sww;
24166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24167             }
24168         }
24169     },
24170
24171     /**
24172      * Hides this shadow
24173      */
24174     hide : function(){
24175         if(this.el){
24176             this.el.dom.style.display = "none";
24177             Roo.Shadow.Pool.push(this.el);
24178             delete this.el;
24179         }
24180     },
24181
24182     /**
24183      * Adjust the z-index of this shadow
24184      * @param {Number} zindex The new z-index
24185      */
24186     setZIndex : function(z){
24187         this.zIndex = z;
24188         if(this.el){
24189             this.el.setStyle("z-index", z);
24190         }
24191     }
24192 };
24193
24194 // Private utility class that manages the internal Shadow cache
24195 Roo.Shadow.Pool = function(){
24196     var p = [];
24197     var markup = Roo.isIE ?
24198                  '<div class="x-ie-shadow"></div>' :
24199                  '<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>';
24200     return {
24201         pull : function(){
24202             var sh = p.shift();
24203             if(!sh){
24204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24205                 sh.autoBoxAdjust = false;
24206             }
24207             return sh;
24208         },
24209
24210         push : function(sh){
24211             p.push(sh);
24212         }
24213     };
24214 }();/*
24215  * Based on:
24216  * Ext JS Library 1.1.1
24217  * Copyright(c) 2006-2007, Ext JS, LLC.
24218  *
24219  * Originally Released Under LGPL - original licence link has changed is not relivant.
24220  *
24221  * Fork - LGPL
24222  * <script type="text/javascript">
24223  */
24224
24225
24226 /**
24227  * @class Roo.SplitBar
24228  * @extends Roo.util.Observable
24229  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24230  * <br><br>
24231  * Usage:
24232  * <pre><code>
24233 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24234                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24235 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24236 split.minSize = 100;
24237 split.maxSize = 600;
24238 split.animate = true;
24239 split.on('moved', splitterMoved);
24240 </code></pre>
24241  * @constructor
24242  * Create a new SplitBar
24243  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24244  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24245  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24246  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24247                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24248                         position of the SplitBar).
24249  */
24250 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24251     
24252     /** @private */
24253     this.el = Roo.get(dragElement, true);
24254     this.el.dom.unselectable = "on";
24255     /** @private */
24256     this.resizingEl = Roo.get(resizingElement, true);
24257
24258     /**
24259      * @private
24260      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24261      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24262      * @type Number
24263      */
24264     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24265     
24266     /**
24267      * The minimum size of the resizing element. (Defaults to 0)
24268      * @type Number
24269      */
24270     this.minSize = 0;
24271     
24272     /**
24273      * The maximum size of the resizing element. (Defaults to 2000)
24274      * @type Number
24275      */
24276     this.maxSize = 2000;
24277     
24278     /**
24279      * Whether to animate the transition to the new size
24280      * @type Boolean
24281      */
24282     this.animate = false;
24283     
24284     /**
24285      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24286      * @type Boolean
24287      */
24288     this.useShim = false;
24289     
24290     /** @private */
24291     this.shim = null;
24292     
24293     if(!existingProxy){
24294         /** @private */
24295         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24296     }else{
24297         this.proxy = Roo.get(existingProxy).dom;
24298     }
24299     /** @private */
24300     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24301     
24302     /** @private */
24303     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24304     
24305     /** @private */
24306     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24307     
24308     /** @private */
24309     this.dragSpecs = {};
24310     
24311     /**
24312      * @private The adapter to use to positon and resize elements
24313      */
24314     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24315     this.adapter.init(this);
24316     
24317     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24318         /** @private */
24319         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24320         this.el.addClass("x-splitbar-h");
24321     }else{
24322         /** @private */
24323         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24324         this.el.addClass("x-splitbar-v");
24325     }
24326     
24327     this.addEvents({
24328         /**
24329          * @event resize
24330          * Fires when the splitter is moved (alias for {@link #event-moved})
24331          * @param {Roo.SplitBar} this
24332          * @param {Number} newSize the new width or height
24333          */
24334         "resize" : true,
24335         /**
24336          * @event moved
24337          * Fires when the splitter is moved
24338          * @param {Roo.SplitBar} this
24339          * @param {Number} newSize the new width or height
24340          */
24341         "moved" : true,
24342         /**
24343          * @event beforeresize
24344          * Fires before the splitter is dragged
24345          * @param {Roo.SplitBar} this
24346          */
24347         "beforeresize" : true,
24348
24349         "beforeapply" : true
24350     });
24351
24352     Roo.util.Observable.call(this);
24353 };
24354
24355 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24356     onStartProxyDrag : function(x, y){
24357         this.fireEvent("beforeresize", this);
24358         if(!this.overlay){
24359             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24360             o.unselectable();
24361             o.enableDisplayMode("block");
24362             // all splitbars share the same overlay
24363             Roo.SplitBar.prototype.overlay = o;
24364         }
24365         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24366         this.overlay.show();
24367         Roo.get(this.proxy).setDisplayed("block");
24368         var size = this.adapter.getElementSize(this);
24369         this.activeMinSize = this.getMinimumSize();;
24370         this.activeMaxSize = this.getMaximumSize();;
24371         var c1 = size - this.activeMinSize;
24372         var c2 = Math.max(this.activeMaxSize - size, 0);
24373         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24374             this.dd.resetConstraints();
24375             this.dd.setXConstraint(
24376                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24377                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24378             );
24379             this.dd.setYConstraint(0, 0);
24380         }else{
24381             this.dd.resetConstraints();
24382             this.dd.setXConstraint(0, 0);
24383             this.dd.setYConstraint(
24384                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24385                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24386             );
24387          }
24388         this.dragSpecs.startSize = size;
24389         this.dragSpecs.startPoint = [x, y];
24390         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24391     },
24392     
24393     /** 
24394      * @private Called after the drag operation by the DDProxy
24395      */
24396     onEndProxyDrag : function(e){
24397         Roo.get(this.proxy).setDisplayed(false);
24398         var endPoint = Roo.lib.Event.getXY(e);
24399         if(this.overlay){
24400             this.overlay.hide();
24401         }
24402         var newSize;
24403         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24404             newSize = this.dragSpecs.startSize + 
24405                 (this.placement == Roo.SplitBar.LEFT ?
24406                     endPoint[0] - this.dragSpecs.startPoint[0] :
24407                     this.dragSpecs.startPoint[0] - endPoint[0]
24408                 );
24409         }else{
24410             newSize = this.dragSpecs.startSize + 
24411                 (this.placement == Roo.SplitBar.TOP ?
24412                     endPoint[1] - this.dragSpecs.startPoint[1] :
24413                     this.dragSpecs.startPoint[1] - endPoint[1]
24414                 );
24415         }
24416         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24417         if(newSize != this.dragSpecs.startSize){
24418             if(this.fireEvent('beforeapply', this, newSize) !== false){
24419                 this.adapter.setElementSize(this, newSize);
24420                 this.fireEvent("moved", this, newSize);
24421                 this.fireEvent("resize", this, newSize);
24422             }
24423         }
24424     },
24425     
24426     /**
24427      * Get the adapter this SplitBar uses
24428      * @return The adapter object
24429      */
24430     getAdapter : function(){
24431         return this.adapter;
24432     },
24433     
24434     /**
24435      * Set the adapter this SplitBar uses
24436      * @param {Object} adapter A SplitBar adapter object
24437      */
24438     setAdapter : function(adapter){
24439         this.adapter = adapter;
24440         this.adapter.init(this);
24441     },
24442     
24443     /**
24444      * Gets the minimum size for the resizing element
24445      * @return {Number} The minimum size
24446      */
24447     getMinimumSize : function(){
24448         return this.minSize;
24449     },
24450     
24451     /**
24452      * Sets the minimum size for the resizing element
24453      * @param {Number} minSize The minimum size
24454      */
24455     setMinimumSize : function(minSize){
24456         this.minSize = minSize;
24457     },
24458     
24459     /**
24460      * Gets the maximum size for the resizing element
24461      * @return {Number} The maximum size
24462      */
24463     getMaximumSize : function(){
24464         return this.maxSize;
24465     },
24466     
24467     /**
24468      * Sets the maximum size for the resizing element
24469      * @param {Number} maxSize The maximum size
24470      */
24471     setMaximumSize : function(maxSize){
24472         this.maxSize = maxSize;
24473     },
24474     
24475     /**
24476      * Sets the initialize size for the resizing element
24477      * @param {Number} size The initial size
24478      */
24479     setCurrentSize : function(size){
24480         var oldAnimate = this.animate;
24481         this.animate = false;
24482         this.adapter.setElementSize(this, size);
24483         this.animate = oldAnimate;
24484     },
24485     
24486     /**
24487      * Destroy this splitbar. 
24488      * @param {Boolean} removeEl True to remove the element
24489      */
24490     destroy : function(removeEl){
24491         if(this.shim){
24492             this.shim.remove();
24493         }
24494         this.dd.unreg();
24495         this.proxy.parentNode.removeChild(this.proxy);
24496         if(removeEl){
24497             this.el.remove();
24498         }
24499     }
24500 });
24501
24502 /**
24503  * @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.
24504  */
24505 Roo.SplitBar.createProxy = function(dir){
24506     var proxy = new Roo.Element(document.createElement("div"));
24507     proxy.unselectable();
24508     var cls = 'x-splitbar-proxy';
24509     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24510     document.body.appendChild(proxy.dom);
24511     return proxy.dom;
24512 };
24513
24514 /** 
24515  * @class Roo.SplitBar.BasicLayoutAdapter
24516  * Default Adapter. It assumes the splitter and resizing element are not positioned
24517  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24518  */
24519 Roo.SplitBar.BasicLayoutAdapter = function(){
24520 };
24521
24522 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24523     // do nothing for now
24524     init : function(s){
24525     
24526     },
24527     /**
24528      * Called before drag operations to get the current size of the resizing element. 
24529      * @param {Roo.SplitBar} s The SplitBar using this adapter
24530      */
24531      getElementSize : function(s){
24532         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24533             return s.resizingEl.getWidth();
24534         }else{
24535             return s.resizingEl.getHeight();
24536         }
24537     },
24538     
24539     /**
24540      * Called after drag operations to set the size of the resizing element.
24541      * @param {Roo.SplitBar} s The SplitBar using this adapter
24542      * @param {Number} newSize The new size to set
24543      * @param {Function} onComplete A function to be invoked when resizing is complete
24544      */
24545     setElementSize : function(s, newSize, onComplete){
24546         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24547             if(!s.animate){
24548                 s.resizingEl.setWidth(newSize);
24549                 if(onComplete){
24550                     onComplete(s, newSize);
24551                 }
24552             }else{
24553                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24554             }
24555         }else{
24556             
24557             if(!s.animate){
24558                 s.resizingEl.setHeight(newSize);
24559                 if(onComplete){
24560                     onComplete(s, newSize);
24561                 }
24562             }else{
24563                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24564             }
24565         }
24566     }
24567 };
24568
24569 /** 
24570  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24571  * @extends Roo.SplitBar.BasicLayoutAdapter
24572  * Adapter that  moves the splitter element to align with the resized sizing element. 
24573  * Used with an absolute positioned SplitBar.
24574  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24575  * document.body, make sure you assign an id to the body element.
24576  */
24577 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24578     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24579     this.container = Roo.get(container);
24580 };
24581
24582 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24583     init : function(s){
24584         this.basic.init(s);
24585     },
24586     
24587     getElementSize : function(s){
24588         return this.basic.getElementSize(s);
24589     },
24590     
24591     setElementSize : function(s, newSize, onComplete){
24592         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24593     },
24594     
24595     moveSplitter : function(s){
24596         var yes = Roo.SplitBar;
24597         switch(s.placement){
24598             case yes.LEFT:
24599                 s.el.setX(s.resizingEl.getRight());
24600                 break;
24601             case yes.RIGHT:
24602                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24603                 break;
24604             case yes.TOP:
24605                 s.el.setY(s.resizingEl.getBottom());
24606                 break;
24607             case yes.BOTTOM:
24608                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24609                 break;
24610         }
24611     }
24612 };
24613
24614 /**
24615  * Orientation constant - Create a vertical SplitBar
24616  * @static
24617  * @type Number
24618  */
24619 Roo.SplitBar.VERTICAL = 1;
24620
24621 /**
24622  * Orientation constant - Create a horizontal SplitBar
24623  * @static
24624  * @type Number
24625  */
24626 Roo.SplitBar.HORIZONTAL = 2;
24627
24628 /**
24629  * Placement constant - The resizing element is to the left of the splitter element
24630  * @static
24631  * @type Number
24632  */
24633 Roo.SplitBar.LEFT = 1;
24634
24635 /**
24636  * Placement constant - The resizing element is to the right of the splitter element
24637  * @static
24638  * @type Number
24639  */
24640 Roo.SplitBar.RIGHT = 2;
24641
24642 /**
24643  * Placement constant - The resizing element is positioned above the splitter element
24644  * @static
24645  * @type Number
24646  */
24647 Roo.SplitBar.TOP = 3;
24648
24649 /**
24650  * Placement constant - The resizing element is positioned under splitter element
24651  * @static
24652  * @type Number
24653  */
24654 Roo.SplitBar.BOTTOM = 4;
24655 /*
24656  * Based on:
24657  * Ext JS Library 1.1.1
24658  * Copyright(c) 2006-2007, Ext JS, LLC.
24659  *
24660  * Originally Released Under LGPL - original licence link has changed is not relivant.
24661  *
24662  * Fork - LGPL
24663  * <script type="text/javascript">
24664  */
24665
24666 /**
24667  * @class Roo.View
24668  * @extends Roo.util.Observable
24669  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24670  * This class also supports single and multi selection modes. <br>
24671  * Create a data model bound view:
24672  <pre><code>
24673  var store = new Roo.data.Store(...);
24674
24675  var view = new Roo.View({
24676     el : "my-element",
24677     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24678  
24679     singleSelect: true,
24680     selectedClass: "ydataview-selected",
24681     store: store
24682  });
24683
24684  // listen for node click?
24685  view.on("click", function(vw, index, node, e){
24686  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24687  });
24688
24689  // load XML data
24690  dataModel.load("foobar.xml");
24691  </code></pre>
24692  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24693  * <br><br>
24694  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24695  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24696  * 
24697  * Note: old style constructor is still suported (container, template, config)
24698  * 
24699  * @constructor
24700  * Create a new View
24701  * @param {Object} config The config object
24702  * 
24703  */
24704 Roo.View = function(config, depreciated_tpl, depreciated_config){
24705     
24706     if (typeof(depreciated_tpl) == 'undefined') {
24707         // new way.. - universal constructor.
24708         Roo.apply(this, config);
24709         this.el  = Roo.get(this.el);
24710     } else {
24711         // old format..
24712         this.el  = Roo.get(config);
24713         this.tpl = depreciated_tpl;
24714         Roo.apply(this, depreciated_config);
24715     }
24716     this.wrapEl  = this.el.wrap().wrap();
24717     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24718     
24719     
24720     if(typeof(this.tpl) == "string"){
24721         this.tpl = new Roo.Template(this.tpl);
24722     } else {
24723         // support xtype ctors..
24724         this.tpl = new Roo.factory(this.tpl, Roo);
24725     }
24726     
24727     
24728     this.tpl.compile();
24729    
24730   
24731     
24732      
24733     /** @private */
24734     this.addEvents({
24735         /**
24736          * @event beforeclick
24737          * Fires before a click is processed. Returns false to cancel the default action.
24738          * @param {Roo.View} this
24739          * @param {Number} index The index of the target node
24740          * @param {HTMLElement} node The target node
24741          * @param {Roo.EventObject} e The raw event object
24742          */
24743             "beforeclick" : true,
24744         /**
24745          * @event click
24746          * Fires when a template node is clicked.
24747          * @param {Roo.View} this
24748          * @param {Number} index The index of the target node
24749          * @param {HTMLElement} node The target node
24750          * @param {Roo.EventObject} e The raw event object
24751          */
24752             "click" : true,
24753         /**
24754          * @event dblclick
24755          * Fires when a template node is double clicked.
24756          * @param {Roo.View} this
24757          * @param {Number} index The index of the target node
24758          * @param {HTMLElement} node The target node
24759          * @param {Roo.EventObject} e The raw event object
24760          */
24761             "dblclick" : true,
24762         /**
24763          * @event contextmenu
24764          * Fires when a template node is right clicked.
24765          * @param {Roo.View} this
24766          * @param {Number} index The index of the target node
24767          * @param {HTMLElement} node The target node
24768          * @param {Roo.EventObject} e The raw event object
24769          */
24770             "contextmenu" : true,
24771         /**
24772          * @event selectionchange
24773          * Fires when the selected nodes change.
24774          * @param {Roo.View} this
24775          * @param {Array} selections Array of the selected nodes
24776          */
24777             "selectionchange" : true,
24778     
24779         /**
24780          * @event beforeselect
24781          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24782          * @param {Roo.View} this
24783          * @param {HTMLElement} node The node to be selected
24784          * @param {Array} selections Array of currently selected nodes
24785          */
24786             "beforeselect" : true,
24787         /**
24788          * @event preparedata
24789          * Fires on every row to render, to allow you to change the data.
24790          * @param {Roo.View} this
24791          * @param {Object} data to be rendered (change this)
24792          */
24793           "preparedata" : true
24794           
24795           
24796         });
24797
24798
24799
24800     this.el.on({
24801         "click": this.onClick,
24802         "dblclick": this.onDblClick,
24803         "contextmenu": this.onContextMenu,
24804         scope:this
24805     });
24806
24807     this.selections = [];
24808     this.nodes = [];
24809     this.cmp = new Roo.CompositeElementLite([]);
24810     if(this.store){
24811         this.store = Roo.factory(this.store, Roo.data);
24812         this.setStore(this.store, true);
24813     }
24814     
24815     if ( this.footer && this.footer.xtype) {
24816            
24817          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24818         
24819         this.footer.dataSource = this.store
24820         this.footer.container = fctr;
24821         this.footer = Roo.factory(this.footer, Roo);
24822         fctr.insertFirst(this.el);
24823         
24824         // this is a bit insane - as the paging toolbar seems to detach the el..
24825 //        dom.parentNode.parentNode.parentNode
24826          // they get detached?
24827     }
24828     
24829     
24830     Roo.View.superclass.constructor.call(this);
24831     
24832     
24833 };
24834
24835 Roo.extend(Roo.View, Roo.util.Observable, {
24836     
24837      /**
24838      * @cfg {Roo.data.Store} store Data store to load data from.
24839      */
24840     store : false,
24841     
24842     /**
24843      * @cfg {String|Roo.Element} el The container element.
24844      */
24845     el : '',
24846     
24847     /**
24848      * @cfg {String|Roo.Template} tpl The template used by this View 
24849      */
24850     tpl : false,
24851     /**
24852      * @cfg {String} dataName the named area of the template to use as the data area
24853      *                          Works with domtemplates roo-name="name"
24854      */
24855     dataName: false,
24856     /**
24857      * @cfg {String} selectedClass The css class to add to selected nodes
24858      */
24859     selectedClass : "x-view-selected",
24860      /**
24861      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24862      */
24863     emptyText : "",
24864     
24865     /**
24866      * @cfg {String} text to display on mask (default Loading)
24867      */
24868     mask : false,
24869     /**
24870      * @cfg {Boolean} multiSelect Allow multiple selection
24871      */
24872     multiSelect : false,
24873     /**
24874      * @cfg {Boolean} singleSelect Allow single selection
24875      */
24876     singleSelect:  false,
24877     
24878     /**
24879      * @cfg {Boolean} toggleSelect - selecting 
24880      */
24881     toggleSelect : false,
24882     
24883     /**
24884      * Returns the element this view is bound to.
24885      * @return {Roo.Element}
24886      */
24887     getEl : function(){
24888         return this.wrapEl;
24889     },
24890     
24891     
24892
24893     /**
24894      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24895      */
24896     refresh : function(){
24897         var t = this.tpl;
24898         
24899         // if we are using something like 'domtemplate', then
24900         // the what gets used is:
24901         // t.applySubtemplate(NAME, data, wrapping data..)
24902         // the outer template then get' applied with
24903         //     the store 'extra data'
24904         // and the body get's added to the
24905         //      roo-name="data" node?
24906         //      <span class='roo-tpl-{name}'></span> ?????
24907         
24908         
24909         
24910         this.clearSelections();
24911         this.el.update("");
24912         var html = [];
24913         var records = this.store.getRange();
24914         if(records.length < 1) {
24915             
24916             // is this valid??  = should it render a template??
24917             
24918             this.el.update(this.emptyText);
24919             return;
24920         }
24921         var el = this.el;
24922         if (this.dataName) {
24923             this.el.update(t.apply(this.store.meta)); //????
24924             el = this.el.child('.roo-tpl-' + this.dataName);
24925         }
24926         
24927         for(var i = 0, len = records.length; i < len; i++){
24928             var data = this.prepareData(records[i].data, i, records[i]);
24929             this.fireEvent("preparedata", this, data, i, records[i]);
24930             html[html.length] = Roo.util.Format.trim(
24931                 this.dataName ?
24932                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24933                     t.apply(data)
24934             );
24935         }
24936         
24937         
24938         
24939         el.update(html.join(""));
24940         this.nodes = el.dom.childNodes;
24941         this.updateIndexes(0);
24942     },
24943
24944     /**
24945      * Function to override to reformat the data that is sent to
24946      * the template for each node.
24947      * DEPRICATED - use the preparedata event handler.
24948      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24949      * a JSON object for an UpdateManager bound view).
24950      */
24951     prepareData : function(data, index, record)
24952     {
24953         this.fireEvent("preparedata", this, data, index, record);
24954         return data;
24955     },
24956
24957     onUpdate : function(ds, record){
24958         this.clearSelections();
24959         var index = this.store.indexOf(record);
24960         var n = this.nodes[index];
24961         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24962         n.parentNode.removeChild(n);
24963         this.updateIndexes(index, index);
24964     },
24965
24966     
24967     
24968 // --------- FIXME     
24969     onAdd : function(ds, records, index)
24970     {
24971         this.clearSelections();
24972         if(this.nodes.length == 0){
24973             this.refresh();
24974             return;
24975         }
24976         var n = this.nodes[index];
24977         for(var i = 0, len = records.length; i < len; i++){
24978             var d = this.prepareData(records[i].data, i, records[i]);
24979             if(n){
24980                 this.tpl.insertBefore(n, d);
24981             }else{
24982                 
24983                 this.tpl.append(this.el, d);
24984             }
24985         }
24986         this.updateIndexes(index);
24987     },
24988
24989     onRemove : function(ds, record, index){
24990         this.clearSelections();
24991         var el = this.dataName  ?
24992             this.el.child('.roo-tpl-' + this.dataName) :
24993             this.el; 
24994         el.dom.removeChild(this.nodes[index]);
24995         this.updateIndexes(index);
24996     },
24997
24998     /**
24999      * Refresh an individual node.
25000      * @param {Number} index
25001      */
25002     refreshNode : function(index){
25003         this.onUpdate(this.store, this.store.getAt(index));
25004     },
25005
25006     updateIndexes : function(startIndex, endIndex){
25007         var ns = this.nodes;
25008         startIndex = startIndex || 0;
25009         endIndex = endIndex || ns.length - 1;
25010         for(var i = startIndex; i <= endIndex; i++){
25011             ns[i].nodeIndex = i;
25012         }
25013     },
25014
25015     /**
25016      * Changes the data store this view uses and refresh the view.
25017      * @param {Store} store
25018      */
25019     setStore : function(store, initial){
25020         if(!initial && this.store){
25021             this.store.un("datachanged", this.refresh);
25022             this.store.un("add", this.onAdd);
25023             this.store.un("remove", this.onRemove);
25024             this.store.un("update", this.onUpdate);
25025             this.store.un("clear", this.refresh);
25026             this.store.un("beforeload", this.onBeforeLoad);
25027             this.store.un("load", this.onLoad);
25028             this.store.un("loadexception", this.onLoad);
25029         }
25030         if(store){
25031           
25032             store.on("datachanged", this.refresh, this);
25033             store.on("add", this.onAdd, this);
25034             store.on("remove", this.onRemove, this);
25035             store.on("update", this.onUpdate, this);
25036             store.on("clear", this.refresh, this);
25037             store.on("beforeload", this.onBeforeLoad, this);
25038             store.on("load", this.onLoad, this);
25039             store.on("loadexception", this.onLoad, this);
25040         }
25041         
25042         if(store){
25043             this.refresh();
25044         }
25045     },
25046     /**
25047      * onbeforeLoad - masks the loading area.
25048      *
25049      */
25050     onBeforeLoad : function()
25051     {
25052         this.el.update("");
25053         this.el.mask(this.mask ? this.mask : "Loading" ); 
25054     },
25055     onLoad : function ()
25056     {
25057         this.el.unmask();
25058     },
25059     
25060
25061     /**
25062      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25063      * @param {HTMLElement} node
25064      * @return {HTMLElement} The template node
25065      */
25066     findItemFromChild : function(node){
25067         var el = this.dataName  ?
25068             this.el.child('.roo-tpl-' + this.dataName,true) :
25069             this.el.dom; 
25070         
25071         if(!node || node.parentNode == el){
25072                     return node;
25073             }
25074             var p = node.parentNode;
25075             while(p && p != el){
25076             if(p.parentNode == el){
25077                 return p;
25078             }
25079             p = p.parentNode;
25080         }
25081             return null;
25082     },
25083
25084     /** @ignore */
25085     onClick : function(e){
25086         var item = this.findItemFromChild(e.getTarget());
25087         if(item){
25088             var index = this.indexOf(item);
25089             if(this.onItemClick(item, index, e) !== false){
25090                 this.fireEvent("click", this, index, item, e);
25091             }
25092         }else{
25093             this.clearSelections();
25094         }
25095     },
25096
25097     /** @ignore */
25098     onContextMenu : function(e){
25099         var item = this.findItemFromChild(e.getTarget());
25100         if(item){
25101             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25102         }
25103     },
25104
25105     /** @ignore */
25106     onDblClick : function(e){
25107         var item = this.findItemFromChild(e.getTarget());
25108         if(item){
25109             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25110         }
25111     },
25112
25113     onItemClick : function(item, index, e)
25114     {
25115         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25116             return false;
25117         }
25118         if (this.toggleSelect) {
25119             var m = this.isSelected(item) ? 'unselect' : 'select';
25120             Roo.log(m);
25121             var _t = this;
25122             _t[m](item, true, false);
25123             return true;
25124         }
25125         if(this.multiSelect || this.singleSelect){
25126             if(this.multiSelect && e.shiftKey && this.lastSelection){
25127                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25128             }else{
25129                 this.select(item, this.multiSelect && e.ctrlKey);
25130                 this.lastSelection = item;
25131             }
25132             e.preventDefault();
25133         }
25134         return true;
25135     },
25136
25137     /**
25138      * Get the number of selected nodes.
25139      * @return {Number}
25140      */
25141     getSelectionCount : function(){
25142         return this.selections.length;
25143     },
25144
25145     /**
25146      * Get the currently selected nodes.
25147      * @return {Array} An array of HTMLElements
25148      */
25149     getSelectedNodes : function(){
25150         return this.selections;
25151     },
25152
25153     /**
25154      * Get the indexes of the selected nodes.
25155      * @return {Array}
25156      */
25157     getSelectedIndexes : function(){
25158         var indexes = [], s = this.selections;
25159         for(var i = 0, len = s.length; i < len; i++){
25160             indexes.push(s[i].nodeIndex);
25161         }
25162         return indexes;
25163     },
25164
25165     /**
25166      * Clear all selections
25167      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25168      */
25169     clearSelections : function(suppressEvent){
25170         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25171             this.cmp.elements = this.selections;
25172             this.cmp.removeClass(this.selectedClass);
25173             this.selections = [];
25174             if(!suppressEvent){
25175                 this.fireEvent("selectionchange", this, this.selections);
25176             }
25177         }
25178     },
25179
25180     /**
25181      * Returns true if the passed node is selected
25182      * @param {HTMLElement/Number} node The node or node index
25183      * @return {Boolean}
25184      */
25185     isSelected : function(node){
25186         var s = this.selections;
25187         if(s.length < 1){
25188             return false;
25189         }
25190         node = this.getNode(node);
25191         return s.indexOf(node) !== -1;
25192     },
25193
25194     /**
25195      * Selects nodes.
25196      * @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
25197      * @param {Boolean} keepExisting (optional) true to keep existing selections
25198      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25199      */
25200     select : function(nodeInfo, keepExisting, suppressEvent){
25201         if(nodeInfo instanceof Array){
25202             if(!keepExisting){
25203                 this.clearSelections(true);
25204             }
25205             for(var i = 0, len = nodeInfo.length; i < len; i++){
25206                 this.select(nodeInfo[i], true, true);
25207             }
25208             return;
25209         } 
25210         var node = this.getNode(nodeInfo);
25211         if(!node || this.isSelected(node)){
25212             return; // already selected.
25213         }
25214         if(!keepExisting){
25215             this.clearSelections(true);
25216         }
25217         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25218             Roo.fly(node).addClass(this.selectedClass);
25219             this.selections.push(node);
25220             if(!suppressEvent){
25221                 this.fireEvent("selectionchange", this, this.selections);
25222             }
25223         }
25224         
25225         
25226     },
25227       /**
25228      * Unselects nodes.
25229      * @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
25230      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25231      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25232      */
25233     unselect : function(nodeInfo, keepExisting, suppressEvent)
25234     {
25235         if(nodeInfo instanceof Array){
25236             Roo.each(this.selections, function(s) {
25237                 this.unselect(s, nodeInfo);
25238             }, this);
25239             return;
25240         }
25241         var node = this.getNode(nodeInfo);
25242         if(!node || !this.isSelected(node)){
25243             Roo.log("not selected");
25244             return; // not selected.
25245         }
25246         // fireevent???
25247         var ns = [];
25248         Roo.each(this.selections, function(s) {
25249             if (s == node ) {
25250                 Roo.fly(node).removeClass(this.selectedClass);
25251
25252                 return;
25253             }
25254             ns.push(s);
25255         },this);
25256         
25257         this.selections= ns;
25258         this.fireEvent("selectionchange", this, this.selections);
25259     },
25260
25261     /**
25262      * Gets a template node.
25263      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25264      * @return {HTMLElement} The node or null if it wasn't found
25265      */
25266     getNode : function(nodeInfo){
25267         if(typeof nodeInfo == "string"){
25268             return document.getElementById(nodeInfo);
25269         }else if(typeof nodeInfo == "number"){
25270             return this.nodes[nodeInfo];
25271         }
25272         return nodeInfo;
25273     },
25274
25275     /**
25276      * Gets a range template nodes.
25277      * @param {Number} startIndex
25278      * @param {Number} endIndex
25279      * @return {Array} An array of nodes
25280      */
25281     getNodes : function(start, end){
25282         var ns = this.nodes;
25283         start = start || 0;
25284         end = typeof end == "undefined" ? ns.length - 1 : end;
25285         var nodes = [];
25286         if(start <= end){
25287             for(var i = start; i <= end; i++){
25288                 nodes.push(ns[i]);
25289             }
25290         } else{
25291             for(var i = start; i >= end; i--){
25292                 nodes.push(ns[i]);
25293             }
25294         }
25295         return nodes;
25296     },
25297
25298     /**
25299      * Finds the index of the passed node
25300      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25301      * @return {Number} The index of the node or -1
25302      */
25303     indexOf : function(node){
25304         node = this.getNode(node);
25305         if(typeof node.nodeIndex == "number"){
25306             return node.nodeIndex;
25307         }
25308         var ns = this.nodes;
25309         for(var i = 0, len = ns.length; i < len; i++){
25310             if(ns[i] == node){
25311                 return i;
25312             }
25313         }
25314         return -1;
25315     }
25316 });
25317 /*
25318  * Based on:
25319  * Ext JS Library 1.1.1
25320  * Copyright(c) 2006-2007, Ext JS, LLC.
25321  *
25322  * Originally Released Under LGPL - original licence link has changed is not relivant.
25323  *
25324  * Fork - LGPL
25325  * <script type="text/javascript">
25326  */
25327
25328 /**
25329  * @class Roo.JsonView
25330  * @extends Roo.View
25331  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25332 <pre><code>
25333 var view = new Roo.JsonView({
25334     container: "my-element",
25335     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25336     multiSelect: true, 
25337     jsonRoot: "data" 
25338 });
25339
25340 // listen for node click?
25341 view.on("click", function(vw, index, node, e){
25342     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25343 });
25344
25345 // direct load of JSON data
25346 view.load("foobar.php");
25347
25348 // Example from my blog list
25349 var tpl = new Roo.Template(
25350     '&lt;div class="entry"&gt;' +
25351     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25352     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25353     "&lt;/div&gt;&lt;hr /&gt;"
25354 );
25355
25356 var moreView = new Roo.JsonView({
25357     container :  "entry-list", 
25358     template : tpl,
25359     jsonRoot: "posts"
25360 });
25361 moreView.on("beforerender", this.sortEntries, this);
25362 moreView.load({
25363     url: "/blog/get-posts.php",
25364     params: "allposts=true",
25365     text: "Loading Blog Entries..."
25366 });
25367 </code></pre>
25368
25369 * Note: old code is supported with arguments : (container, template, config)
25370
25371
25372  * @constructor
25373  * Create a new JsonView
25374  * 
25375  * @param {Object} config The config object
25376  * 
25377  */
25378 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25379     
25380     
25381     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25382
25383     var um = this.el.getUpdateManager();
25384     um.setRenderer(this);
25385     um.on("update", this.onLoad, this);
25386     um.on("failure", this.onLoadException, this);
25387
25388     /**
25389      * @event beforerender
25390      * Fires before rendering of the downloaded JSON data.
25391      * @param {Roo.JsonView} this
25392      * @param {Object} data The JSON data loaded
25393      */
25394     /**
25395      * @event load
25396      * Fires when data is loaded.
25397      * @param {Roo.JsonView} this
25398      * @param {Object} data The JSON data loaded
25399      * @param {Object} response The raw Connect response object
25400      */
25401     /**
25402      * @event loadexception
25403      * Fires when loading fails.
25404      * @param {Roo.JsonView} this
25405      * @param {Object} response The raw Connect response object
25406      */
25407     this.addEvents({
25408         'beforerender' : true,
25409         'load' : true,
25410         'loadexception' : true
25411     });
25412 };
25413 Roo.extend(Roo.JsonView, Roo.View, {
25414     /**
25415      * @type {String} The root property in the loaded JSON object that contains the data
25416      */
25417     jsonRoot : "",
25418
25419     /**
25420      * Refreshes the view.
25421      */
25422     refresh : function(){
25423         this.clearSelections();
25424         this.el.update("");
25425         var html = [];
25426         var o = this.jsonData;
25427         if(o && o.length > 0){
25428             for(var i = 0, len = o.length; i < len; i++){
25429                 var data = this.prepareData(o[i], i, o);
25430                 html[html.length] = this.tpl.apply(data);
25431             }
25432         }else{
25433             html.push(this.emptyText);
25434         }
25435         this.el.update(html.join(""));
25436         this.nodes = this.el.dom.childNodes;
25437         this.updateIndexes(0);
25438     },
25439
25440     /**
25441      * 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.
25442      * @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:
25443      <pre><code>
25444      view.load({
25445          url: "your-url.php",
25446          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25447          callback: yourFunction,
25448          scope: yourObject, //(optional scope)
25449          discardUrl: false,
25450          nocache: false,
25451          text: "Loading...",
25452          timeout: 30,
25453          scripts: false
25454      });
25455      </code></pre>
25456      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25457      * 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.
25458      * @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}
25459      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25460      * @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.
25461      */
25462     load : function(){
25463         var um = this.el.getUpdateManager();
25464         um.update.apply(um, arguments);
25465     },
25466
25467     render : function(el, response){
25468         this.clearSelections();
25469         this.el.update("");
25470         var o;
25471         try{
25472             o = Roo.util.JSON.decode(response.responseText);
25473             if(this.jsonRoot){
25474                 
25475                 o = o[this.jsonRoot];
25476             }
25477         } catch(e){
25478         }
25479         /**
25480          * The current JSON data or null
25481          */
25482         this.jsonData = o;
25483         this.beforeRender();
25484         this.refresh();
25485     },
25486
25487 /**
25488  * Get the number of records in the current JSON dataset
25489  * @return {Number}
25490  */
25491     getCount : function(){
25492         return this.jsonData ? this.jsonData.length : 0;
25493     },
25494
25495 /**
25496  * Returns the JSON object for the specified node(s)
25497  * @param {HTMLElement/Array} node The node or an array of nodes
25498  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25499  * you get the JSON object for the node
25500  */
25501     getNodeData : function(node){
25502         if(node instanceof Array){
25503             var data = [];
25504             for(var i = 0, len = node.length; i < len; i++){
25505                 data.push(this.getNodeData(node[i]));
25506             }
25507             return data;
25508         }
25509         return this.jsonData[this.indexOf(node)] || null;
25510     },
25511
25512     beforeRender : function(){
25513         this.snapshot = this.jsonData;
25514         if(this.sortInfo){
25515             this.sort.apply(this, this.sortInfo);
25516         }
25517         this.fireEvent("beforerender", this, this.jsonData);
25518     },
25519
25520     onLoad : function(el, o){
25521         this.fireEvent("load", this, this.jsonData, o);
25522     },
25523
25524     onLoadException : function(el, o){
25525         this.fireEvent("loadexception", this, o);
25526     },
25527
25528 /**
25529  * Filter the data by a specific property.
25530  * @param {String} property A property on your JSON objects
25531  * @param {String/RegExp} value Either string that the property values
25532  * should start with, or a RegExp to test against the property
25533  */
25534     filter : function(property, value){
25535         if(this.jsonData){
25536             var data = [];
25537             var ss = this.snapshot;
25538             if(typeof value == "string"){
25539                 var vlen = value.length;
25540                 if(vlen == 0){
25541                     this.clearFilter();
25542                     return;
25543                 }
25544                 value = value.toLowerCase();
25545                 for(var i = 0, len = ss.length; i < len; i++){
25546                     var o = ss[i];
25547                     if(o[property].substr(0, vlen).toLowerCase() == value){
25548                         data.push(o);
25549                     }
25550                 }
25551             } else if(value.exec){ // regex?
25552                 for(var i = 0, len = ss.length; i < len; i++){
25553                     var o = ss[i];
25554                     if(value.test(o[property])){
25555                         data.push(o);
25556                     }
25557                 }
25558             } else{
25559                 return;
25560             }
25561             this.jsonData = data;
25562             this.refresh();
25563         }
25564     },
25565
25566 /**
25567  * Filter by a function. The passed function will be called with each
25568  * object in the current dataset. If the function returns true the value is kept,
25569  * otherwise it is filtered.
25570  * @param {Function} fn
25571  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25572  */
25573     filterBy : function(fn, scope){
25574         if(this.jsonData){
25575             var data = [];
25576             var ss = this.snapshot;
25577             for(var i = 0, len = ss.length; i < len; i++){
25578                 var o = ss[i];
25579                 if(fn.call(scope || this, o)){
25580                     data.push(o);
25581                 }
25582             }
25583             this.jsonData = data;
25584             this.refresh();
25585         }
25586     },
25587
25588 /**
25589  * Clears the current filter.
25590  */
25591     clearFilter : function(){
25592         if(this.snapshot && this.jsonData != this.snapshot){
25593             this.jsonData = this.snapshot;
25594             this.refresh();
25595         }
25596     },
25597
25598
25599 /**
25600  * Sorts the data for this view and refreshes it.
25601  * @param {String} property A property on your JSON objects to sort on
25602  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25603  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25604  */
25605     sort : function(property, dir, sortType){
25606         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25607         if(this.jsonData){
25608             var p = property;
25609             var dsc = dir && dir.toLowerCase() == "desc";
25610             var f = function(o1, o2){
25611                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25612                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25613                 ;
25614                 if(v1 < v2){
25615                     return dsc ? +1 : -1;
25616                 } else if(v1 > v2){
25617                     return dsc ? -1 : +1;
25618                 } else{
25619                     return 0;
25620                 }
25621             };
25622             this.jsonData.sort(f);
25623             this.refresh();
25624             if(this.jsonData != this.snapshot){
25625                 this.snapshot.sort(f);
25626             }
25627         }
25628     }
25629 });/*
25630  * Based on:
25631  * Ext JS Library 1.1.1
25632  * Copyright(c) 2006-2007, Ext JS, LLC.
25633  *
25634  * Originally Released Under LGPL - original licence link has changed is not relivant.
25635  *
25636  * Fork - LGPL
25637  * <script type="text/javascript">
25638  */
25639  
25640
25641 /**
25642  * @class Roo.ColorPalette
25643  * @extends Roo.Component
25644  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25645  * Here's an example of typical usage:
25646  * <pre><code>
25647 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25648 cp.render('my-div');
25649
25650 cp.on('select', function(palette, selColor){
25651     // do something with selColor
25652 });
25653 </code></pre>
25654  * @constructor
25655  * Create a new ColorPalette
25656  * @param {Object} config The config object
25657  */
25658 Roo.ColorPalette = function(config){
25659     Roo.ColorPalette.superclass.constructor.call(this, config);
25660     this.addEvents({
25661         /**
25662              * @event select
25663              * Fires when a color is selected
25664              * @param {ColorPalette} this
25665              * @param {String} color The 6-digit color hex code (without the # symbol)
25666              */
25667         select: true
25668     });
25669
25670     if(this.handler){
25671         this.on("select", this.handler, this.scope, true);
25672     }
25673 };
25674 Roo.extend(Roo.ColorPalette, Roo.Component, {
25675     /**
25676      * @cfg {String} itemCls
25677      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25678      */
25679     itemCls : "x-color-palette",
25680     /**
25681      * @cfg {String} value
25682      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25683      * the hex codes are case-sensitive.
25684      */
25685     value : null,
25686     clickEvent:'click',
25687     // private
25688     ctype: "Roo.ColorPalette",
25689
25690     /**
25691      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25692      */
25693     allowReselect : false,
25694
25695     /**
25696      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25697      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25698      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25699      * of colors with the width setting until the box is symmetrical.</p>
25700      * <p>You can override individual colors if needed:</p>
25701      * <pre><code>
25702 var cp = new Roo.ColorPalette();
25703 cp.colors[0] = "FF0000";  // change the first box to red
25704 </code></pre>
25705
25706 Or you can provide a custom array of your own for complete control:
25707 <pre><code>
25708 var cp = new Roo.ColorPalette();
25709 cp.colors = ["000000", "993300", "333300"];
25710 </code></pre>
25711      * @type Array
25712      */
25713     colors : [
25714         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25715         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25716         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25717         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25718         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25719     ],
25720
25721     // private
25722     onRender : function(container, position){
25723         var t = new Roo.MasterTemplate(
25724             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25725         );
25726         var c = this.colors;
25727         for(var i = 0, len = c.length; i < len; i++){
25728             t.add([c[i]]);
25729         }
25730         var el = document.createElement("div");
25731         el.className = this.itemCls;
25732         t.overwrite(el);
25733         container.dom.insertBefore(el, position);
25734         this.el = Roo.get(el);
25735         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25736         if(this.clickEvent != 'click'){
25737             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25738         }
25739     },
25740
25741     // private
25742     afterRender : function(){
25743         Roo.ColorPalette.superclass.afterRender.call(this);
25744         if(this.value){
25745             var s = this.value;
25746             this.value = null;
25747             this.select(s);
25748         }
25749     },
25750
25751     // private
25752     handleClick : function(e, t){
25753         e.preventDefault();
25754         if(!this.disabled){
25755             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25756             this.select(c.toUpperCase());
25757         }
25758     },
25759
25760     /**
25761      * Selects the specified color in the palette (fires the select event)
25762      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25763      */
25764     select : function(color){
25765         color = color.replace("#", "");
25766         if(color != this.value || this.allowReselect){
25767             var el = this.el;
25768             if(this.value){
25769                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25770             }
25771             el.child("a.color-"+color).addClass("x-color-palette-sel");
25772             this.value = color;
25773             this.fireEvent("select", this, color);
25774         }
25775     }
25776 });/*
25777  * Based on:
25778  * Ext JS Library 1.1.1
25779  * Copyright(c) 2006-2007, Ext JS, LLC.
25780  *
25781  * Originally Released Under LGPL - original licence link has changed is not relivant.
25782  *
25783  * Fork - LGPL
25784  * <script type="text/javascript">
25785  */
25786  
25787 /**
25788  * @class Roo.DatePicker
25789  * @extends Roo.Component
25790  * Simple date picker class.
25791  * @constructor
25792  * Create a new DatePicker
25793  * @param {Object} config The config object
25794  */
25795 Roo.DatePicker = function(config){
25796     Roo.DatePicker.superclass.constructor.call(this, config);
25797
25798     this.value = config && config.value ?
25799                  config.value.clearTime() : new Date().clearTime();
25800
25801     this.addEvents({
25802         /**
25803              * @event select
25804              * Fires when a date is selected
25805              * @param {DatePicker} this
25806              * @param {Date} date The selected date
25807              */
25808         'select': true,
25809         /**
25810              * @event monthchange
25811              * Fires when the displayed month changes 
25812              * @param {DatePicker} this
25813              * @param {Date} date The selected month
25814              */
25815         'monthchange': true
25816     });
25817
25818     if(this.handler){
25819         this.on("select", this.handler,  this.scope || this);
25820     }
25821     // build the disabledDatesRE
25822     if(!this.disabledDatesRE && this.disabledDates){
25823         var dd = this.disabledDates;
25824         var re = "(?:";
25825         for(var i = 0; i < dd.length; i++){
25826             re += dd[i];
25827             if(i != dd.length-1) re += "|";
25828         }
25829         this.disabledDatesRE = new RegExp(re + ")");
25830     }
25831 };
25832
25833 Roo.extend(Roo.DatePicker, Roo.Component, {
25834     /**
25835      * @cfg {String} todayText
25836      * The text to display on the button that selects the current date (defaults to "Today")
25837      */
25838     todayText : "Today",
25839     /**
25840      * @cfg {String} okText
25841      * The text to display on the ok button
25842      */
25843     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25844     /**
25845      * @cfg {String} cancelText
25846      * The text to display on the cancel button
25847      */
25848     cancelText : "Cancel",
25849     /**
25850      * @cfg {String} todayTip
25851      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25852      */
25853     todayTip : "{0} (Spacebar)",
25854     /**
25855      * @cfg {Date} minDate
25856      * Minimum allowable date (JavaScript date object, defaults to null)
25857      */
25858     minDate : null,
25859     /**
25860      * @cfg {Date} maxDate
25861      * Maximum allowable date (JavaScript date object, defaults to null)
25862      */
25863     maxDate : null,
25864     /**
25865      * @cfg {String} minText
25866      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25867      */
25868     minText : "This date is before the minimum date",
25869     /**
25870      * @cfg {String} maxText
25871      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25872      */
25873     maxText : "This date is after the maximum date",
25874     /**
25875      * @cfg {String} format
25876      * The default date format string which can be overriden for localization support.  The format must be
25877      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25878      */
25879     format : "m/d/y",
25880     /**
25881      * @cfg {Array} disabledDays
25882      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25883      */
25884     disabledDays : null,
25885     /**
25886      * @cfg {String} disabledDaysText
25887      * The tooltip to display when the date falls on a disabled day (defaults to "")
25888      */
25889     disabledDaysText : "",
25890     /**
25891      * @cfg {RegExp} disabledDatesRE
25892      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25893      */
25894     disabledDatesRE : null,
25895     /**
25896      * @cfg {String} disabledDatesText
25897      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25898      */
25899     disabledDatesText : "",
25900     /**
25901      * @cfg {Boolean} constrainToViewport
25902      * True to constrain the date picker to the viewport (defaults to true)
25903      */
25904     constrainToViewport : true,
25905     /**
25906      * @cfg {Array} monthNames
25907      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25908      */
25909     monthNames : Date.monthNames,
25910     /**
25911      * @cfg {Array} dayNames
25912      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25913      */
25914     dayNames : Date.dayNames,
25915     /**
25916      * @cfg {String} nextText
25917      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25918      */
25919     nextText: 'Next Month (Control+Right)',
25920     /**
25921      * @cfg {String} prevText
25922      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25923      */
25924     prevText: 'Previous Month (Control+Left)',
25925     /**
25926      * @cfg {String} monthYearText
25927      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25928      */
25929     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25930     /**
25931      * @cfg {Number} startDay
25932      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25933      */
25934     startDay : 0,
25935     /**
25936      * @cfg {Bool} showClear
25937      * Show a clear button (usefull for date form elements that can be blank.)
25938      */
25939     
25940     showClear: false,
25941     
25942     /**
25943      * Sets the value of the date field
25944      * @param {Date} value The date to set
25945      */
25946     setValue : function(value){
25947         var old = this.value;
25948         
25949         if (typeof(value) == 'string') {
25950          
25951             value = Date.parseDate(value, this.format);
25952         }
25953         if (!value) {
25954             value = new Date();
25955         }
25956         
25957         this.value = value.clearTime(true);
25958         if(this.el){
25959             this.update(this.value);
25960         }
25961     },
25962
25963     /**
25964      * Gets the current selected value of the date field
25965      * @return {Date} The selected date
25966      */
25967     getValue : function(){
25968         return this.value;
25969     },
25970
25971     // private
25972     focus : function(){
25973         if(this.el){
25974             this.update(this.activeDate);
25975         }
25976     },
25977
25978     // privateval
25979     onRender : function(container, position){
25980         
25981         var m = [
25982              '<table cellspacing="0">',
25983                 '<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>',
25984                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25985         var dn = this.dayNames;
25986         for(var i = 0; i < 7; i++){
25987             var d = this.startDay+i;
25988             if(d > 6){
25989                 d = d-7;
25990             }
25991             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25992         }
25993         m[m.length] = "</tr></thead><tbody><tr>";
25994         for(var i = 0; i < 42; i++) {
25995             if(i % 7 == 0 && i != 0){
25996                 m[m.length] = "</tr><tr>";
25997             }
25998             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25999         }
26000         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26001             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26002
26003         var el = document.createElement("div");
26004         el.className = "x-date-picker";
26005         el.innerHTML = m.join("");
26006
26007         container.dom.insertBefore(el, position);
26008
26009         this.el = Roo.get(el);
26010         this.eventEl = Roo.get(el.firstChild);
26011
26012         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26013             handler: this.showPrevMonth,
26014             scope: this,
26015             preventDefault:true,
26016             stopDefault:true
26017         });
26018
26019         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26020             handler: this.showNextMonth,
26021             scope: this,
26022             preventDefault:true,
26023             stopDefault:true
26024         });
26025
26026         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26027
26028         this.monthPicker = this.el.down('div.x-date-mp');
26029         this.monthPicker.enableDisplayMode('block');
26030         
26031         var kn = new Roo.KeyNav(this.eventEl, {
26032             "left" : function(e){
26033                 e.ctrlKey ?
26034                     this.showPrevMonth() :
26035                     this.update(this.activeDate.add("d", -1));
26036             },
26037
26038             "right" : function(e){
26039                 e.ctrlKey ?
26040                     this.showNextMonth() :
26041                     this.update(this.activeDate.add("d", 1));
26042             },
26043
26044             "up" : function(e){
26045                 e.ctrlKey ?
26046                     this.showNextYear() :
26047                     this.update(this.activeDate.add("d", -7));
26048             },
26049
26050             "down" : function(e){
26051                 e.ctrlKey ?
26052                     this.showPrevYear() :
26053                     this.update(this.activeDate.add("d", 7));
26054             },
26055
26056             "pageUp" : function(e){
26057                 this.showNextMonth();
26058             },
26059
26060             "pageDown" : function(e){
26061                 this.showPrevMonth();
26062             },
26063
26064             "enter" : function(e){
26065                 e.stopPropagation();
26066                 return true;
26067             },
26068
26069             scope : this
26070         });
26071
26072         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26073
26074         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26075
26076         this.el.unselectable();
26077         
26078         this.cells = this.el.select("table.x-date-inner tbody td");
26079         this.textNodes = this.el.query("table.x-date-inner tbody span");
26080
26081         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26082             text: "&#160;",
26083             tooltip: this.monthYearText
26084         });
26085
26086         this.mbtn.on('click', this.showMonthPicker, this);
26087         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26088
26089
26090         var today = (new Date()).dateFormat(this.format);
26091         
26092         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26093         if (this.showClear) {
26094             baseTb.add( new Roo.Toolbar.Fill());
26095         }
26096         baseTb.add({
26097             text: String.format(this.todayText, today),
26098             tooltip: String.format(this.todayTip, today),
26099             handler: this.selectToday,
26100             scope: this
26101         });
26102         
26103         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26104             
26105         //});
26106         if (this.showClear) {
26107             
26108             baseTb.add( new Roo.Toolbar.Fill());
26109             baseTb.add({
26110                 text: '&#160;',
26111                 cls: 'x-btn-icon x-btn-clear',
26112                 handler: function() {
26113                     //this.value = '';
26114                     this.fireEvent("select", this, '');
26115                 },
26116                 scope: this
26117             });
26118         }
26119         
26120         
26121         if(Roo.isIE){
26122             this.el.repaint();
26123         }
26124         this.update(this.value);
26125     },
26126
26127     createMonthPicker : function(){
26128         if(!this.monthPicker.dom.firstChild){
26129             var buf = ['<table border="0" cellspacing="0">'];
26130             for(var i = 0; i < 6; i++){
26131                 buf.push(
26132                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26133                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26134                     i == 0 ?
26135                     '<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>' :
26136                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26137                 );
26138             }
26139             buf.push(
26140                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26141                     this.okText,
26142                     '</button><button type="button" class="x-date-mp-cancel">',
26143                     this.cancelText,
26144                     '</button></td></tr>',
26145                 '</table>'
26146             );
26147             this.monthPicker.update(buf.join(''));
26148             this.monthPicker.on('click', this.onMonthClick, this);
26149             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26150
26151             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26152             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26153
26154             this.mpMonths.each(function(m, a, i){
26155                 i += 1;
26156                 if((i%2) == 0){
26157                     m.dom.xmonth = 5 + Math.round(i * .5);
26158                 }else{
26159                     m.dom.xmonth = Math.round((i-1) * .5);
26160                 }
26161             });
26162         }
26163     },
26164
26165     showMonthPicker : function(){
26166         this.createMonthPicker();
26167         var size = this.el.getSize();
26168         this.monthPicker.setSize(size);
26169         this.monthPicker.child('table').setSize(size);
26170
26171         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26172         this.updateMPMonth(this.mpSelMonth);
26173         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26174         this.updateMPYear(this.mpSelYear);
26175
26176         this.monthPicker.slideIn('t', {duration:.2});
26177     },
26178
26179     updateMPYear : function(y){
26180         this.mpyear = y;
26181         var ys = this.mpYears.elements;
26182         for(var i = 1; i <= 10; i++){
26183             var td = ys[i-1], y2;
26184             if((i%2) == 0){
26185                 y2 = y + Math.round(i * .5);
26186                 td.firstChild.innerHTML = y2;
26187                 td.xyear = y2;
26188             }else{
26189                 y2 = y - (5-Math.round(i * .5));
26190                 td.firstChild.innerHTML = y2;
26191                 td.xyear = y2;
26192             }
26193             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26194         }
26195     },
26196
26197     updateMPMonth : function(sm){
26198         this.mpMonths.each(function(m, a, i){
26199             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26200         });
26201     },
26202
26203     selectMPMonth: function(m){
26204         
26205     },
26206
26207     onMonthClick : function(e, t){
26208         e.stopEvent();
26209         var el = new Roo.Element(t), pn;
26210         if(el.is('button.x-date-mp-cancel')){
26211             this.hideMonthPicker();
26212         }
26213         else if(el.is('button.x-date-mp-ok')){
26214             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26215             this.hideMonthPicker();
26216         }
26217         else if(pn = el.up('td.x-date-mp-month', 2)){
26218             this.mpMonths.removeClass('x-date-mp-sel');
26219             pn.addClass('x-date-mp-sel');
26220             this.mpSelMonth = pn.dom.xmonth;
26221         }
26222         else if(pn = el.up('td.x-date-mp-year', 2)){
26223             this.mpYears.removeClass('x-date-mp-sel');
26224             pn.addClass('x-date-mp-sel');
26225             this.mpSelYear = pn.dom.xyear;
26226         }
26227         else if(el.is('a.x-date-mp-prev')){
26228             this.updateMPYear(this.mpyear-10);
26229         }
26230         else if(el.is('a.x-date-mp-next')){
26231             this.updateMPYear(this.mpyear+10);
26232         }
26233     },
26234
26235     onMonthDblClick : function(e, t){
26236         e.stopEvent();
26237         var el = new Roo.Element(t), pn;
26238         if(pn = el.up('td.x-date-mp-month', 2)){
26239             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26240             this.hideMonthPicker();
26241         }
26242         else if(pn = el.up('td.x-date-mp-year', 2)){
26243             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26244             this.hideMonthPicker();
26245         }
26246     },
26247
26248     hideMonthPicker : function(disableAnim){
26249         if(this.monthPicker){
26250             if(disableAnim === true){
26251                 this.monthPicker.hide();
26252             }else{
26253                 this.monthPicker.slideOut('t', {duration:.2});
26254             }
26255         }
26256     },
26257
26258     // private
26259     showPrevMonth : function(e){
26260         this.update(this.activeDate.add("mo", -1));
26261     },
26262
26263     // private
26264     showNextMonth : function(e){
26265         this.update(this.activeDate.add("mo", 1));
26266     },
26267
26268     // private
26269     showPrevYear : function(){
26270         this.update(this.activeDate.add("y", -1));
26271     },
26272
26273     // private
26274     showNextYear : function(){
26275         this.update(this.activeDate.add("y", 1));
26276     },
26277
26278     // private
26279     handleMouseWheel : function(e){
26280         var delta = e.getWheelDelta();
26281         if(delta > 0){
26282             this.showPrevMonth();
26283             e.stopEvent();
26284         } else if(delta < 0){
26285             this.showNextMonth();
26286             e.stopEvent();
26287         }
26288     },
26289
26290     // private
26291     handleDateClick : function(e, t){
26292         e.stopEvent();
26293         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26294             this.setValue(new Date(t.dateValue));
26295             this.fireEvent("select", this, this.value);
26296         }
26297     },
26298
26299     // private
26300     selectToday : function(){
26301         this.setValue(new Date().clearTime());
26302         this.fireEvent("select", this, this.value);
26303     },
26304
26305     // private
26306     update : function(date)
26307     {
26308         var vd = this.activeDate;
26309         this.activeDate = date;
26310         if(vd && this.el){
26311             var t = date.getTime();
26312             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26313                 this.cells.removeClass("x-date-selected");
26314                 this.cells.each(function(c){
26315                    if(c.dom.firstChild.dateValue == t){
26316                        c.addClass("x-date-selected");
26317                        setTimeout(function(){
26318                             try{c.dom.firstChild.focus();}catch(e){}
26319                        }, 50);
26320                        return false;
26321                    }
26322                 });
26323                 return;
26324             }
26325         }
26326         
26327         var days = date.getDaysInMonth();
26328         var firstOfMonth = date.getFirstDateOfMonth();
26329         var startingPos = firstOfMonth.getDay()-this.startDay;
26330
26331         if(startingPos <= this.startDay){
26332             startingPos += 7;
26333         }
26334
26335         var pm = date.add("mo", -1);
26336         var prevStart = pm.getDaysInMonth()-startingPos;
26337
26338         var cells = this.cells.elements;
26339         var textEls = this.textNodes;
26340         days += startingPos;
26341
26342         // convert everything to numbers so it's fast
26343         var day = 86400000;
26344         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26345         var today = new Date().clearTime().getTime();
26346         var sel = date.clearTime().getTime();
26347         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26348         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26349         var ddMatch = this.disabledDatesRE;
26350         var ddText = this.disabledDatesText;
26351         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26352         var ddaysText = this.disabledDaysText;
26353         var format = this.format;
26354
26355         var setCellClass = function(cal, cell){
26356             cell.title = "";
26357             var t = d.getTime();
26358             cell.firstChild.dateValue = t;
26359             if(t == today){
26360                 cell.className += " x-date-today";
26361                 cell.title = cal.todayText;
26362             }
26363             if(t == sel){
26364                 cell.className += " x-date-selected";
26365                 setTimeout(function(){
26366                     try{cell.firstChild.focus();}catch(e){}
26367                 }, 50);
26368             }
26369             // disabling
26370             if(t < min) {
26371                 cell.className = " x-date-disabled";
26372                 cell.title = cal.minText;
26373                 return;
26374             }
26375             if(t > max) {
26376                 cell.className = " x-date-disabled";
26377                 cell.title = cal.maxText;
26378                 return;
26379             }
26380             if(ddays){
26381                 if(ddays.indexOf(d.getDay()) != -1){
26382                     cell.title = ddaysText;
26383                     cell.className = " x-date-disabled";
26384                 }
26385             }
26386             if(ddMatch && format){
26387                 var fvalue = d.dateFormat(format);
26388                 if(ddMatch.test(fvalue)){
26389                     cell.title = ddText.replace("%0", fvalue);
26390                     cell.className = " x-date-disabled";
26391                 }
26392             }
26393         };
26394
26395         var i = 0;
26396         for(; i < startingPos; i++) {
26397             textEls[i].innerHTML = (++prevStart);
26398             d.setDate(d.getDate()+1);
26399             cells[i].className = "x-date-prevday";
26400             setCellClass(this, cells[i]);
26401         }
26402         for(; i < days; i++){
26403             intDay = i - startingPos + 1;
26404             textEls[i].innerHTML = (intDay);
26405             d.setDate(d.getDate()+1);
26406             cells[i].className = "x-date-active";
26407             setCellClass(this, cells[i]);
26408         }
26409         var extraDays = 0;
26410         for(; i < 42; i++) {
26411              textEls[i].innerHTML = (++extraDays);
26412              d.setDate(d.getDate()+1);
26413              cells[i].className = "x-date-nextday";
26414              setCellClass(this, cells[i]);
26415         }
26416
26417         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26418         this.fireEvent('monthchange', this, date);
26419         
26420         if(!this.internalRender){
26421             var main = this.el.dom.firstChild;
26422             var w = main.offsetWidth;
26423             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26424             Roo.fly(main).setWidth(w);
26425             this.internalRender = true;
26426             // opera does not respect the auto grow header center column
26427             // then, after it gets a width opera refuses to recalculate
26428             // without a second pass
26429             if(Roo.isOpera && !this.secondPass){
26430                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26431                 this.secondPass = true;
26432                 this.update.defer(10, this, [date]);
26433             }
26434         }
26435         
26436         
26437     }
26438 });        /*
26439  * Based on:
26440  * Ext JS Library 1.1.1
26441  * Copyright(c) 2006-2007, Ext JS, LLC.
26442  *
26443  * Originally Released Under LGPL - original licence link has changed is not relivant.
26444  *
26445  * Fork - LGPL
26446  * <script type="text/javascript">
26447  */
26448 /**
26449  * @class Roo.TabPanel
26450  * @extends Roo.util.Observable
26451  * A lightweight tab container.
26452  * <br><br>
26453  * Usage:
26454  * <pre><code>
26455 // basic tabs 1, built from existing content
26456 var tabs = new Roo.TabPanel("tabs1");
26457 tabs.addTab("script", "View Script");
26458 tabs.addTab("markup", "View Markup");
26459 tabs.activate("script");
26460
26461 // more advanced tabs, built from javascript
26462 var jtabs = new Roo.TabPanel("jtabs");
26463 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26464
26465 // set up the UpdateManager
26466 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26467 var updater = tab2.getUpdateManager();
26468 updater.setDefaultUrl("ajax1.htm");
26469 tab2.on('activate', updater.refresh, updater, true);
26470
26471 // Use setUrl for Ajax loading
26472 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26473 tab3.setUrl("ajax2.htm", null, true);
26474
26475 // Disabled tab
26476 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26477 tab4.disable();
26478
26479 jtabs.activate("jtabs-1");
26480  * </code></pre>
26481  * @constructor
26482  * Create a new TabPanel.
26483  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26484  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26485  */
26486 Roo.TabPanel = function(container, config){
26487     /**
26488     * The container element for this TabPanel.
26489     * @type Roo.Element
26490     */
26491     this.el = Roo.get(container, true);
26492     if(config){
26493         if(typeof config == "boolean"){
26494             this.tabPosition = config ? "bottom" : "top";
26495         }else{
26496             Roo.apply(this, config);
26497         }
26498     }
26499     if(this.tabPosition == "bottom"){
26500         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26501         this.el.addClass("x-tabs-bottom");
26502     }
26503     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26504     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26505     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26506     if(Roo.isIE){
26507         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26508     }
26509     if(this.tabPosition != "bottom"){
26510         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26511          * @type Roo.Element
26512          */
26513         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26514         this.el.addClass("x-tabs-top");
26515     }
26516     this.items = [];
26517
26518     this.bodyEl.setStyle("position", "relative");
26519
26520     this.active = null;
26521     this.activateDelegate = this.activate.createDelegate(this);
26522
26523     this.addEvents({
26524         /**
26525          * @event tabchange
26526          * Fires when the active tab changes
26527          * @param {Roo.TabPanel} this
26528          * @param {Roo.TabPanelItem} activePanel The new active tab
26529          */
26530         "tabchange": true,
26531         /**
26532          * @event beforetabchange
26533          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26534          * @param {Roo.TabPanel} this
26535          * @param {Object} e Set cancel to true on this object to cancel the tab change
26536          * @param {Roo.TabPanelItem} tab The tab being changed to
26537          */
26538         "beforetabchange" : true
26539     });
26540
26541     Roo.EventManager.onWindowResize(this.onResize, this);
26542     this.cpad = this.el.getPadding("lr");
26543     this.hiddenCount = 0;
26544
26545
26546     // toolbar on the tabbar support...
26547     if (this.toolbar) {
26548         var tcfg = this.toolbar;
26549         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26550         this.toolbar = new Roo.Toolbar(tcfg);
26551         if (Roo.isSafari) {
26552             var tbl = tcfg.container.child('table', true);
26553             tbl.setAttribute('width', '100%');
26554         }
26555         
26556     }
26557    
26558
26559
26560     Roo.TabPanel.superclass.constructor.call(this);
26561 };
26562
26563 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26564     /*
26565      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26566      */
26567     tabPosition : "top",
26568     /*
26569      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26570      */
26571     currentTabWidth : 0,
26572     /*
26573      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26574      */
26575     minTabWidth : 40,
26576     /*
26577      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26578      */
26579     maxTabWidth : 250,
26580     /*
26581      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26582      */
26583     preferredTabWidth : 175,
26584     /*
26585      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26586      */
26587     resizeTabs : false,
26588     /*
26589      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26590      */
26591     monitorResize : true,
26592     /*
26593      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26594      */
26595     toolbar : false,
26596
26597     /**
26598      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26599      * @param {String} id The id of the div to use <b>or create</b>
26600      * @param {String} text The text for the tab
26601      * @param {String} content (optional) Content to put in the TabPanelItem body
26602      * @param {Boolean} closable (optional) True to create a close icon on the tab
26603      * @return {Roo.TabPanelItem} The created TabPanelItem
26604      */
26605     addTab : function(id, text, content, closable){
26606         var item = new Roo.TabPanelItem(this, id, text, closable);
26607         this.addTabItem(item);
26608         if(content){
26609             item.setContent(content);
26610         }
26611         return item;
26612     },
26613
26614     /**
26615      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26616      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26617      * @return {Roo.TabPanelItem}
26618      */
26619     getTab : function(id){
26620         return this.items[id];
26621     },
26622
26623     /**
26624      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26625      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26626      */
26627     hideTab : function(id){
26628         var t = this.items[id];
26629         if(!t.isHidden()){
26630            t.setHidden(true);
26631            this.hiddenCount++;
26632            this.autoSizeTabs();
26633         }
26634     },
26635
26636     /**
26637      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26638      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26639      */
26640     unhideTab : function(id){
26641         var t = this.items[id];
26642         if(t.isHidden()){
26643            t.setHidden(false);
26644            this.hiddenCount--;
26645            this.autoSizeTabs();
26646         }
26647     },
26648
26649     /**
26650      * Adds an existing {@link Roo.TabPanelItem}.
26651      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26652      */
26653     addTabItem : function(item){
26654         this.items[item.id] = item;
26655         this.items.push(item);
26656         if(this.resizeTabs){
26657            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26658            this.autoSizeTabs();
26659         }else{
26660             item.autoSize();
26661         }
26662     },
26663
26664     /**
26665      * Removes a {@link Roo.TabPanelItem}.
26666      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26667      */
26668     removeTab : function(id){
26669         var items = this.items;
26670         var tab = items[id];
26671         if(!tab) { return; }
26672         var index = items.indexOf(tab);
26673         if(this.active == tab && items.length > 1){
26674             var newTab = this.getNextAvailable(index);
26675             if(newTab) {
26676                 newTab.activate();
26677             }
26678         }
26679         this.stripEl.dom.removeChild(tab.pnode.dom);
26680         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26681             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26682         }
26683         items.splice(index, 1);
26684         delete this.items[tab.id];
26685         tab.fireEvent("close", tab);
26686         tab.purgeListeners();
26687         this.autoSizeTabs();
26688     },
26689
26690     getNextAvailable : function(start){
26691         var items = this.items;
26692         var index = start;
26693         // look for a next tab that will slide over to
26694         // replace the one being removed
26695         while(index < items.length){
26696             var item = items[++index];
26697             if(item && !item.isHidden()){
26698                 return item;
26699             }
26700         }
26701         // if one isn't found select the previous tab (on the left)
26702         index = start;
26703         while(index >= 0){
26704             var item = items[--index];
26705             if(item && !item.isHidden()){
26706                 return item;
26707             }
26708         }
26709         return null;
26710     },
26711
26712     /**
26713      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26714      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26715      */
26716     disableTab : function(id){
26717         var tab = this.items[id];
26718         if(tab && this.active != tab){
26719             tab.disable();
26720         }
26721     },
26722
26723     /**
26724      * Enables a {@link Roo.TabPanelItem} that is disabled.
26725      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26726      */
26727     enableTab : function(id){
26728         var tab = this.items[id];
26729         tab.enable();
26730     },
26731
26732     /**
26733      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26734      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26735      * @return {Roo.TabPanelItem} The TabPanelItem.
26736      */
26737     activate : function(id){
26738         var tab = this.items[id];
26739         if(!tab){
26740             return null;
26741         }
26742         if(tab == this.active || tab.disabled){
26743             return tab;
26744         }
26745         var e = {};
26746         this.fireEvent("beforetabchange", this, e, tab);
26747         if(e.cancel !== true && !tab.disabled){
26748             if(this.active){
26749                 this.active.hide();
26750             }
26751             this.active = this.items[id];
26752             this.active.show();
26753             this.fireEvent("tabchange", this, this.active);
26754         }
26755         return tab;
26756     },
26757
26758     /**
26759      * Gets the active {@link Roo.TabPanelItem}.
26760      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26761      */
26762     getActiveTab : function(){
26763         return this.active;
26764     },
26765
26766     /**
26767      * Updates the tab body element to fit the height of the container element
26768      * for overflow scrolling
26769      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26770      */
26771     syncHeight : function(targetHeight){
26772         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26773         var bm = this.bodyEl.getMargins();
26774         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26775         this.bodyEl.setHeight(newHeight);
26776         return newHeight;
26777     },
26778
26779     onResize : function(){
26780         if(this.monitorResize){
26781             this.autoSizeTabs();
26782         }
26783     },
26784
26785     /**
26786      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26787      */
26788     beginUpdate : function(){
26789         this.updating = true;
26790     },
26791
26792     /**
26793      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26794      */
26795     endUpdate : function(){
26796         this.updating = false;
26797         this.autoSizeTabs();
26798     },
26799
26800     /**
26801      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26802      */
26803     autoSizeTabs : function(){
26804         var count = this.items.length;
26805         var vcount = count - this.hiddenCount;
26806         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26807         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26808         var availWidth = Math.floor(w / vcount);
26809         var b = this.stripBody;
26810         if(b.getWidth() > w){
26811             var tabs = this.items;
26812             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26813             if(availWidth < this.minTabWidth){
26814                 /*if(!this.sleft){    // incomplete scrolling code
26815                     this.createScrollButtons();
26816                 }
26817                 this.showScroll();
26818                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26819             }
26820         }else{
26821             if(this.currentTabWidth < this.preferredTabWidth){
26822                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26823             }
26824         }
26825     },
26826
26827     /**
26828      * Returns the number of tabs in this TabPanel.
26829      * @return {Number}
26830      */
26831      getCount : function(){
26832          return this.items.length;
26833      },
26834
26835     /**
26836      * Resizes all the tabs to the passed width
26837      * @param {Number} The new width
26838      */
26839     setTabWidth : function(width){
26840         this.currentTabWidth = width;
26841         for(var i = 0, len = this.items.length; i < len; i++) {
26842                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26843         }
26844     },
26845
26846     /**
26847      * Destroys this TabPanel
26848      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26849      */
26850     destroy : function(removeEl){
26851         Roo.EventManager.removeResizeListener(this.onResize, this);
26852         for(var i = 0, len = this.items.length; i < len; i++){
26853             this.items[i].purgeListeners();
26854         }
26855         if(removeEl === true){
26856             this.el.update("");
26857             this.el.remove();
26858         }
26859     }
26860 });
26861
26862 /**
26863  * @class Roo.TabPanelItem
26864  * @extends Roo.util.Observable
26865  * Represents an individual item (tab plus body) in a TabPanel.
26866  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26867  * @param {String} id The id of this TabPanelItem
26868  * @param {String} text The text for the tab of this TabPanelItem
26869  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26870  */
26871 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26872     /**
26873      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26874      * @type Roo.TabPanel
26875      */
26876     this.tabPanel = tabPanel;
26877     /**
26878      * The id for this TabPanelItem
26879      * @type String
26880      */
26881     this.id = id;
26882     /** @private */
26883     this.disabled = false;
26884     /** @private */
26885     this.text = text;
26886     /** @private */
26887     this.loaded = false;
26888     this.closable = closable;
26889
26890     /**
26891      * The body element for this TabPanelItem.
26892      * @type Roo.Element
26893      */
26894     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26895     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26896     this.bodyEl.setStyle("display", "block");
26897     this.bodyEl.setStyle("zoom", "1");
26898     this.hideAction();
26899
26900     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26901     /** @private */
26902     this.el = Roo.get(els.el, true);
26903     this.inner = Roo.get(els.inner, true);
26904     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26905     this.pnode = Roo.get(els.el.parentNode, true);
26906     this.el.on("mousedown", this.onTabMouseDown, this);
26907     this.el.on("click", this.onTabClick, this);
26908     /** @private */
26909     if(closable){
26910         var c = Roo.get(els.close, true);
26911         c.dom.title = this.closeText;
26912         c.addClassOnOver("close-over");
26913         c.on("click", this.closeClick, this);
26914      }
26915
26916     this.addEvents({
26917          /**
26918          * @event activate
26919          * Fires when this tab becomes the active tab.
26920          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26921          * @param {Roo.TabPanelItem} this
26922          */
26923         "activate": true,
26924         /**
26925          * @event beforeclose
26926          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26927          * @param {Roo.TabPanelItem} this
26928          * @param {Object} e Set cancel to true on this object to cancel the close.
26929          */
26930         "beforeclose": true,
26931         /**
26932          * @event close
26933          * Fires when this tab is closed.
26934          * @param {Roo.TabPanelItem} this
26935          */
26936          "close": true,
26937         /**
26938          * @event deactivate
26939          * Fires when this tab is no longer the active tab.
26940          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26941          * @param {Roo.TabPanelItem} this
26942          */
26943          "deactivate" : true
26944     });
26945     this.hidden = false;
26946
26947     Roo.TabPanelItem.superclass.constructor.call(this);
26948 };
26949
26950 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26951     purgeListeners : function(){
26952        Roo.util.Observable.prototype.purgeListeners.call(this);
26953        this.el.removeAllListeners();
26954     },
26955     /**
26956      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26957      */
26958     show : function(){
26959         this.pnode.addClass("on");
26960         this.showAction();
26961         if(Roo.isOpera){
26962             this.tabPanel.stripWrap.repaint();
26963         }
26964         this.fireEvent("activate", this.tabPanel, this);
26965     },
26966
26967     /**
26968      * Returns true if this tab is the active tab.
26969      * @return {Boolean}
26970      */
26971     isActive : function(){
26972         return this.tabPanel.getActiveTab() == this;
26973     },
26974
26975     /**
26976      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26977      */
26978     hide : function(){
26979         this.pnode.removeClass("on");
26980         this.hideAction();
26981         this.fireEvent("deactivate", this.tabPanel, this);
26982     },
26983
26984     hideAction : function(){
26985         this.bodyEl.hide();
26986         this.bodyEl.setStyle("position", "absolute");
26987         this.bodyEl.setLeft("-20000px");
26988         this.bodyEl.setTop("-20000px");
26989     },
26990
26991     showAction : function(){
26992         this.bodyEl.setStyle("position", "relative");
26993         this.bodyEl.setTop("");
26994         this.bodyEl.setLeft("");
26995         this.bodyEl.show();
26996     },
26997
26998     /**
26999      * Set the tooltip for the tab.
27000      * @param {String} tooltip The tab's tooltip
27001      */
27002     setTooltip : function(text){
27003         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27004             this.textEl.dom.qtip = text;
27005             this.textEl.dom.removeAttribute('title');
27006         }else{
27007             this.textEl.dom.title = text;
27008         }
27009     },
27010
27011     onTabClick : function(e){
27012         e.preventDefault();
27013         this.tabPanel.activate(this.id);
27014     },
27015
27016     onTabMouseDown : function(e){
27017         e.preventDefault();
27018         this.tabPanel.activate(this.id);
27019     },
27020
27021     getWidth : function(){
27022         return this.inner.getWidth();
27023     },
27024
27025     setWidth : function(width){
27026         var iwidth = width - this.pnode.getPadding("lr");
27027         this.inner.setWidth(iwidth);
27028         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27029         this.pnode.setWidth(width);
27030     },
27031
27032     /**
27033      * Show or hide the tab
27034      * @param {Boolean} hidden True to hide or false to show.
27035      */
27036     setHidden : function(hidden){
27037         this.hidden = hidden;
27038         this.pnode.setStyle("display", hidden ? "none" : "");
27039     },
27040
27041     /**
27042      * Returns true if this tab is "hidden"
27043      * @return {Boolean}
27044      */
27045     isHidden : function(){
27046         return this.hidden;
27047     },
27048
27049     /**
27050      * Returns the text for this tab
27051      * @return {String}
27052      */
27053     getText : function(){
27054         return this.text;
27055     },
27056
27057     autoSize : function(){
27058         //this.el.beginMeasure();
27059         this.textEl.setWidth(1);
27060         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27061         //this.el.endMeasure();
27062     },
27063
27064     /**
27065      * Sets the text for the tab (Note: this also sets the tooltip text)
27066      * @param {String} text The tab's text and tooltip
27067      */
27068     setText : function(text){
27069         this.text = text;
27070         this.textEl.update(text);
27071         this.setTooltip(text);
27072         if(!this.tabPanel.resizeTabs){
27073             this.autoSize();
27074         }
27075     },
27076     /**
27077      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27078      */
27079     activate : function(){
27080         this.tabPanel.activate(this.id);
27081     },
27082
27083     /**
27084      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27085      */
27086     disable : function(){
27087         if(this.tabPanel.active != this){
27088             this.disabled = true;
27089             this.pnode.addClass("disabled");
27090         }
27091     },
27092
27093     /**
27094      * Enables this TabPanelItem if it was previously disabled.
27095      */
27096     enable : function(){
27097         this.disabled = false;
27098         this.pnode.removeClass("disabled");
27099     },
27100
27101     /**
27102      * Sets the content for this TabPanelItem.
27103      * @param {String} content The content
27104      * @param {Boolean} loadScripts true to look for and load scripts
27105      */
27106     setContent : function(content, loadScripts){
27107         this.bodyEl.update(content, loadScripts);
27108     },
27109
27110     /**
27111      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27112      * @return {Roo.UpdateManager} The UpdateManager
27113      */
27114     getUpdateManager : function(){
27115         return this.bodyEl.getUpdateManager();
27116     },
27117
27118     /**
27119      * Set a URL to be used to load the content for this TabPanelItem.
27120      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27121      * @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)
27122      * @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)
27123      * @return {Roo.UpdateManager} The UpdateManager
27124      */
27125     setUrl : function(url, params, loadOnce){
27126         if(this.refreshDelegate){
27127             this.un('activate', this.refreshDelegate);
27128         }
27129         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27130         this.on("activate", this.refreshDelegate);
27131         return this.bodyEl.getUpdateManager();
27132     },
27133
27134     /** @private */
27135     _handleRefresh : function(url, params, loadOnce){
27136         if(!loadOnce || !this.loaded){
27137             var updater = this.bodyEl.getUpdateManager();
27138             updater.update(url, params, this._setLoaded.createDelegate(this));
27139         }
27140     },
27141
27142     /**
27143      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27144      *   Will fail silently if the setUrl method has not been called.
27145      *   This does not activate the panel, just updates its content.
27146      */
27147     refresh : function(){
27148         if(this.refreshDelegate){
27149            this.loaded = false;
27150            this.refreshDelegate();
27151         }
27152     },
27153
27154     /** @private */
27155     _setLoaded : function(){
27156         this.loaded = true;
27157     },
27158
27159     /** @private */
27160     closeClick : function(e){
27161         var o = {};
27162         e.stopEvent();
27163         this.fireEvent("beforeclose", this, o);
27164         if(o.cancel !== true){
27165             this.tabPanel.removeTab(this.id);
27166         }
27167     },
27168     /**
27169      * The text displayed in the tooltip for the close icon.
27170      * @type String
27171      */
27172     closeText : "Close this tab"
27173 });
27174
27175 /** @private */
27176 Roo.TabPanel.prototype.createStrip = function(container){
27177     var strip = document.createElement("div");
27178     strip.className = "x-tabs-wrap";
27179     container.appendChild(strip);
27180     return strip;
27181 };
27182 /** @private */
27183 Roo.TabPanel.prototype.createStripList = function(strip){
27184     // div wrapper for retard IE
27185     // returns the "tr" element.
27186     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27187         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27188         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27189     return strip.firstChild.firstChild.firstChild.firstChild;
27190 };
27191 /** @private */
27192 Roo.TabPanel.prototype.createBody = function(container){
27193     var body = document.createElement("div");
27194     Roo.id(body, "tab-body");
27195     Roo.fly(body).addClass("x-tabs-body");
27196     container.appendChild(body);
27197     return body;
27198 };
27199 /** @private */
27200 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27201     var body = Roo.getDom(id);
27202     if(!body){
27203         body = document.createElement("div");
27204         body.id = id;
27205     }
27206     Roo.fly(body).addClass("x-tabs-item-body");
27207     bodyEl.insertBefore(body, bodyEl.firstChild);
27208     return body;
27209 };
27210 /** @private */
27211 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27212     var td = document.createElement("td");
27213     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27214     //stripEl.appendChild(td);
27215     if(closable){
27216         td.className = "x-tabs-closable";
27217         if(!this.closeTpl){
27218             this.closeTpl = new Roo.Template(
27219                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27220                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27221                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27222             );
27223         }
27224         var el = this.closeTpl.overwrite(td, {"text": text});
27225         var close = el.getElementsByTagName("div")[0];
27226         var inner = el.getElementsByTagName("em")[0];
27227         return {"el": el, "close": close, "inner": inner};
27228     } else {
27229         if(!this.tabTpl){
27230             this.tabTpl = new Roo.Template(
27231                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27232                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27233             );
27234         }
27235         var el = this.tabTpl.overwrite(td, {"text": text});
27236         var inner = el.getElementsByTagName("em")[0];
27237         return {"el": el, "inner": inner};
27238     }
27239 };/*
27240  * Based on:
27241  * Ext JS Library 1.1.1
27242  * Copyright(c) 2006-2007, Ext JS, LLC.
27243  *
27244  * Originally Released Under LGPL - original licence link has changed is not relivant.
27245  *
27246  * Fork - LGPL
27247  * <script type="text/javascript">
27248  */
27249
27250 /**
27251  * @class Roo.Button
27252  * @extends Roo.util.Observable
27253  * Simple Button class
27254  * @cfg {String} text The button text
27255  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27256  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27257  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27258  * @cfg {Object} scope The scope of the handler
27259  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27260  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27261  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27262  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27263  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27264  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27265    applies if enableToggle = true)
27266  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27267  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27268   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27269  * @constructor
27270  * Create a new button
27271  * @param {Object} config The config object
27272  */
27273 Roo.Button = function(renderTo, config)
27274 {
27275     if (!config) {
27276         config = renderTo;
27277         renderTo = config.renderTo || false;
27278     }
27279     
27280     Roo.apply(this, config);
27281     this.addEvents({
27282         /**
27283              * @event click
27284              * Fires when this button is clicked
27285              * @param {Button} this
27286              * @param {EventObject} e The click event
27287              */
27288             "click" : true,
27289         /**
27290              * @event toggle
27291              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27292              * @param {Button} this
27293              * @param {Boolean} pressed
27294              */
27295             "toggle" : true,
27296         /**
27297              * @event mouseover
27298              * Fires when the mouse hovers over the button
27299              * @param {Button} this
27300              * @param {Event} e The event object
27301              */
27302         'mouseover' : true,
27303         /**
27304              * @event mouseout
27305              * Fires when the mouse exits the button
27306              * @param {Button} this
27307              * @param {Event} e The event object
27308              */
27309         'mouseout': true,
27310          /**
27311              * @event render
27312              * Fires when the button is rendered
27313              * @param {Button} this
27314              */
27315         'render': true
27316     });
27317     if(this.menu){
27318         this.menu = Roo.menu.MenuMgr.get(this.menu);
27319     }
27320     // register listeners first!!  - so render can be captured..
27321     Roo.util.Observable.call(this);
27322     if(renderTo){
27323         this.render(renderTo);
27324     }
27325     
27326   
27327 };
27328
27329 Roo.extend(Roo.Button, Roo.util.Observable, {
27330     /**
27331      * 
27332      */
27333     
27334     /**
27335      * Read-only. True if this button is hidden
27336      * @type Boolean
27337      */
27338     hidden : false,
27339     /**
27340      * Read-only. True if this button is disabled
27341      * @type Boolean
27342      */
27343     disabled : false,
27344     /**
27345      * Read-only. True if this button is pressed (only if enableToggle = true)
27346      * @type Boolean
27347      */
27348     pressed : false,
27349
27350     /**
27351      * @cfg {Number} tabIndex 
27352      * The DOM tabIndex for this button (defaults to undefined)
27353      */
27354     tabIndex : undefined,
27355
27356     /**
27357      * @cfg {Boolean} enableToggle
27358      * True to enable pressed/not pressed toggling (defaults to false)
27359      */
27360     enableToggle: false,
27361     /**
27362      * @cfg {Mixed} menu
27363      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27364      */
27365     menu : undefined,
27366     /**
27367      * @cfg {String} menuAlign
27368      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27369      */
27370     menuAlign : "tl-bl?",
27371
27372     /**
27373      * @cfg {String} iconCls
27374      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27375      */
27376     iconCls : undefined,
27377     /**
27378      * @cfg {String} type
27379      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27380      */
27381     type : 'button',
27382
27383     // private
27384     menuClassTarget: 'tr',
27385
27386     /**
27387      * @cfg {String} clickEvent
27388      * The type of event to map to the button's event handler (defaults to 'click')
27389      */
27390     clickEvent : 'click',
27391
27392     /**
27393      * @cfg {Boolean} handleMouseEvents
27394      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27395      */
27396     handleMouseEvents : true,
27397
27398     /**
27399      * @cfg {String} tooltipType
27400      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27401      */
27402     tooltipType : 'qtip',
27403
27404     /**
27405      * @cfg {String} cls
27406      * A CSS class to apply to the button's main element.
27407      */
27408     
27409     /**
27410      * @cfg {Roo.Template} template (Optional)
27411      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27412      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27413      * require code modifications if required elements (e.g. a button) aren't present.
27414      */
27415
27416     // private
27417     render : function(renderTo){
27418         var btn;
27419         if(this.hideParent){
27420             this.parentEl = Roo.get(renderTo);
27421         }
27422         if(!this.dhconfig){
27423             if(!this.template){
27424                 if(!Roo.Button.buttonTemplate){
27425                     // hideous table template
27426                     Roo.Button.buttonTemplate = new Roo.Template(
27427                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27428                         '<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>',
27429                         "</tr></tbody></table>");
27430                 }
27431                 this.template = Roo.Button.buttonTemplate;
27432             }
27433             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27434             var btnEl = btn.child("button:first");
27435             btnEl.on('focus', this.onFocus, this);
27436             btnEl.on('blur', this.onBlur, this);
27437             if(this.cls){
27438                 btn.addClass(this.cls);
27439             }
27440             if(this.icon){
27441                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27442             }
27443             if(this.iconCls){
27444                 btnEl.addClass(this.iconCls);
27445                 if(!this.cls){
27446                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27447                 }
27448             }
27449             if(this.tabIndex !== undefined){
27450                 btnEl.dom.tabIndex = this.tabIndex;
27451             }
27452             if(this.tooltip){
27453                 if(typeof this.tooltip == 'object'){
27454                     Roo.QuickTips.tips(Roo.apply({
27455                           target: btnEl.id
27456                     }, this.tooltip));
27457                 } else {
27458                     btnEl.dom[this.tooltipType] = this.tooltip;
27459                 }
27460             }
27461         }else{
27462             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27463         }
27464         this.el = btn;
27465         if(this.id){
27466             this.el.dom.id = this.el.id = this.id;
27467         }
27468         if(this.menu){
27469             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27470             this.menu.on("show", this.onMenuShow, this);
27471             this.menu.on("hide", this.onMenuHide, this);
27472         }
27473         btn.addClass("x-btn");
27474         if(Roo.isIE && !Roo.isIE7){
27475             this.autoWidth.defer(1, this);
27476         }else{
27477             this.autoWidth();
27478         }
27479         if(this.handleMouseEvents){
27480             btn.on("mouseover", this.onMouseOver, this);
27481             btn.on("mouseout", this.onMouseOut, this);
27482             btn.on("mousedown", this.onMouseDown, this);
27483         }
27484         btn.on(this.clickEvent, this.onClick, this);
27485         //btn.on("mouseup", this.onMouseUp, this);
27486         if(this.hidden){
27487             this.hide();
27488         }
27489         if(this.disabled){
27490             this.disable();
27491         }
27492         Roo.ButtonToggleMgr.register(this);
27493         if(this.pressed){
27494             this.el.addClass("x-btn-pressed");
27495         }
27496         if(this.repeat){
27497             var repeater = new Roo.util.ClickRepeater(btn,
27498                 typeof this.repeat == "object" ? this.repeat : {}
27499             );
27500             repeater.on("click", this.onClick,  this);
27501         }
27502         
27503         this.fireEvent('render', this);
27504         
27505     },
27506     /**
27507      * Returns the button's underlying element
27508      * @return {Roo.Element} The element
27509      */
27510     getEl : function(){
27511         return this.el;  
27512     },
27513     
27514     /**
27515      * Destroys this Button and removes any listeners.
27516      */
27517     destroy : function(){
27518         Roo.ButtonToggleMgr.unregister(this);
27519         this.el.removeAllListeners();
27520         this.purgeListeners();
27521         this.el.remove();
27522     },
27523
27524     // private
27525     autoWidth : function(){
27526         if(this.el){
27527             this.el.setWidth("auto");
27528             if(Roo.isIE7 && Roo.isStrict){
27529                 var ib = this.el.child('button');
27530                 if(ib && ib.getWidth() > 20){
27531                     ib.clip();
27532                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27533                 }
27534             }
27535             if(this.minWidth){
27536                 if(this.hidden){
27537                     this.el.beginMeasure();
27538                 }
27539                 if(this.el.getWidth() < this.minWidth){
27540                     this.el.setWidth(this.minWidth);
27541                 }
27542                 if(this.hidden){
27543                     this.el.endMeasure();
27544                 }
27545             }
27546         }
27547     },
27548
27549     /**
27550      * Assigns this button's click handler
27551      * @param {Function} handler The function to call when the button is clicked
27552      * @param {Object} scope (optional) Scope for the function passed in
27553      */
27554     setHandler : function(handler, scope){
27555         this.handler = handler;
27556         this.scope = scope;  
27557     },
27558     
27559     /**
27560      * Sets this button's text
27561      * @param {String} text The button text
27562      */
27563     setText : function(text){
27564         this.text = text;
27565         if(this.el){
27566             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27567         }
27568         this.autoWidth();
27569     },
27570     
27571     /**
27572      * Gets the text for this button
27573      * @return {String} The button text
27574      */
27575     getText : function(){
27576         return this.text;  
27577     },
27578     
27579     /**
27580      * Show this button
27581      */
27582     show: function(){
27583         this.hidden = false;
27584         if(this.el){
27585             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27586         }
27587     },
27588     
27589     /**
27590      * Hide this button
27591      */
27592     hide: function(){
27593         this.hidden = true;
27594         if(this.el){
27595             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27596         }
27597     },
27598     
27599     /**
27600      * Convenience function for boolean show/hide
27601      * @param {Boolean} visible True to show, false to hide
27602      */
27603     setVisible: function(visible){
27604         if(visible) {
27605             this.show();
27606         }else{
27607             this.hide();
27608         }
27609     },
27610     
27611     /**
27612      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27613      * @param {Boolean} state (optional) Force a particular state
27614      */
27615     toggle : function(state){
27616         state = state === undefined ? !this.pressed : state;
27617         if(state != this.pressed){
27618             if(state){
27619                 this.el.addClass("x-btn-pressed");
27620                 this.pressed = true;
27621                 this.fireEvent("toggle", this, true);
27622             }else{
27623                 this.el.removeClass("x-btn-pressed");
27624                 this.pressed = false;
27625                 this.fireEvent("toggle", this, false);
27626             }
27627             if(this.toggleHandler){
27628                 this.toggleHandler.call(this.scope || this, this, state);
27629             }
27630         }
27631     },
27632     
27633     /**
27634      * Focus the button
27635      */
27636     focus : function(){
27637         this.el.child('button:first').focus();
27638     },
27639     
27640     /**
27641      * Disable this button
27642      */
27643     disable : function(){
27644         if(this.el){
27645             this.el.addClass("x-btn-disabled");
27646         }
27647         this.disabled = true;
27648     },
27649     
27650     /**
27651      * Enable this button
27652      */
27653     enable : function(){
27654         if(this.el){
27655             this.el.removeClass("x-btn-disabled");
27656         }
27657         this.disabled = false;
27658     },
27659
27660     /**
27661      * Convenience function for boolean enable/disable
27662      * @param {Boolean} enabled True to enable, false to disable
27663      */
27664     setDisabled : function(v){
27665         this[v !== true ? "enable" : "disable"]();
27666     },
27667
27668     // private
27669     onClick : function(e){
27670         if(e){
27671             e.preventDefault();
27672         }
27673         if(e.button != 0){
27674             return;
27675         }
27676         if(!this.disabled){
27677             if(this.enableToggle){
27678                 this.toggle();
27679             }
27680             if(this.menu && !this.menu.isVisible()){
27681                 this.menu.show(this.el, this.menuAlign);
27682             }
27683             this.fireEvent("click", this, e);
27684             if(this.handler){
27685                 this.el.removeClass("x-btn-over");
27686                 this.handler.call(this.scope || this, this, e);
27687             }
27688         }
27689     },
27690     // private
27691     onMouseOver : function(e){
27692         if(!this.disabled){
27693             this.el.addClass("x-btn-over");
27694             this.fireEvent('mouseover', this, e);
27695         }
27696     },
27697     // private
27698     onMouseOut : function(e){
27699         if(!e.within(this.el,  true)){
27700             this.el.removeClass("x-btn-over");
27701             this.fireEvent('mouseout', this, e);
27702         }
27703     },
27704     // private
27705     onFocus : function(e){
27706         if(!this.disabled){
27707             this.el.addClass("x-btn-focus");
27708         }
27709     },
27710     // private
27711     onBlur : function(e){
27712         this.el.removeClass("x-btn-focus");
27713     },
27714     // private
27715     onMouseDown : function(e){
27716         if(!this.disabled && e.button == 0){
27717             this.el.addClass("x-btn-click");
27718             Roo.get(document).on('mouseup', this.onMouseUp, this);
27719         }
27720     },
27721     // private
27722     onMouseUp : function(e){
27723         if(e.button == 0){
27724             this.el.removeClass("x-btn-click");
27725             Roo.get(document).un('mouseup', this.onMouseUp, this);
27726         }
27727     },
27728     // private
27729     onMenuShow : function(e){
27730         this.el.addClass("x-btn-menu-active");
27731     },
27732     // private
27733     onMenuHide : function(e){
27734         this.el.removeClass("x-btn-menu-active");
27735     }   
27736 });
27737
27738 // Private utility class used by Button
27739 Roo.ButtonToggleMgr = function(){
27740    var groups = {};
27741    
27742    function toggleGroup(btn, state){
27743        if(state){
27744            var g = groups[btn.toggleGroup];
27745            for(var i = 0, l = g.length; i < l; i++){
27746                if(g[i] != btn){
27747                    g[i].toggle(false);
27748                }
27749            }
27750        }
27751    }
27752    
27753    return {
27754        register : function(btn){
27755            if(!btn.toggleGroup){
27756                return;
27757            }
27758            var g = groups[btn.toggleGroup];
27759            if(!g){
27760                g = groups[btn.toggleGroup] = [];
27761            }
27762            g.push(btn);
27763            btn.on("toggle", toggleGroup);
27764        },
27765        
27766        unregister : function(btn){
27767            if(!btn.toggleGroup){
27768                return;
27769            }
27770            var g = groups[btn.toggleGroup];
27771            if(g){
27772                g.remove(btn);
27773                btn.un("toggle", toggleGroup);
27774            }
27775        }
27776    };
27777 }();/*
27778  * Based on:
27779  * Ext JS Library 1.1.1
27780  * Copyright(c) 2006-2007, Ext JS, LLC.
27781  *
27782  * Originally Released Under LGPL - original licence link has changed is not relivant.
27783  *
27784  * Fork - LGPL
27785  * <script type="text/javascript">
27786  */
27787  
27788 /**
27789  * @class Roo.SplitButton
27790  * @extends Roo.Button
27791  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27792  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27793  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27794  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27795  * @cfg {String} arrowTooltip The title attribute of the arrow
27796  * @constructor
27797  * Create a new menu button
27798  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27799  * @param {Object} config The config object
27800  */
27801 Roo.SplitButton = function(renderTo, config){
27802     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27803     /**
27804      * @event arrowclick
27805      * Fires when this button's arrow is clicked
27806      * @param {SplitButton} this
27807      * @param {EventObject} e The click event
27808      */
27809     this.addEvents({"arrowclick":true});
27810 };
27811
27812 Roo.extend(Roo.SplitButton, Roo.Button, {
27813     render : function(renderTo){
27814         // this is one sweet looking template!
27815         var tpl = new Roo.Template(
27816             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27817             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27818             '<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>',
27819             "</tbody></table></td><td>",
27820             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27821             '<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>',
27822             "</tbody></table></td></tr></table>"
27823         );
27824         var btn = tpl.append(renderTo, [this.text, this.type], true);
27825         var btnEl = btn.child("button");
27826         if(this.cls){
27827             btn.addClass(this.cls);
27828         }
27829         if(this.icon){
27830             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27831         }
27832         if(this.iconCls){
27833             btnEl.addClass(this.iconCls);
27834             if(!this.cls){
27835                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27836             }
27837         }
27838         this.el = btn;
27839         if(this.handleMouseEvents){
27840             btn.on("mouseover", this.onMouseOver, this);
27841             btn.on("mouseout", this.onMouseOut, this);
27842             btn.on("mousedown", this.onMouseDown, this);
27843             btn.on("mouseup", this.onMouseUp, this);
27844         }
27845         btn.on(this.clickEvent, this.onClick, this);
27846         if(this.tooltip){
27847             if(typeof this.tooltip == 'object'){
27848                 Roo.QuickTips.tips(Roo.apply({
27849                       target: btnEl.id
27850                 }, this.tooltip));
27851             } else {
27852                 btnEl.dom[this.tooltipType] = this.tooltip;
27853             }
27854         }
27855         if(this.arrowTooltip){
27856             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27857         }
27858         if(this.hidden){
27859             this.hide();
27860         }
27861         if(this.disabled){
27862             this.disable();
27863         }
27864         if(this.pressed){
27865             this.el.addClass("x-btn-pressed");
27866         }
27867         if(Roo.isIE && !Roo.isIE7){
27868             this.autoWidth.defer(1, this);
27869         }else{
27870             this.autoWidth();
27871         }
27872         if(this.menu){
27873             this.menu.on("show", this.onMenuShow, this);
27874             this.menu.on("hide", this.onMenuHide, this);
27875         }
27876         this.fireEvent('render', this);
27877     },
27878
27879     // private
27880     autoWidth : function(){
27881         if(this.el){
27882             var tbl = this.el.child("table:first");
27883             var tbl2 = this.el.child("table:last");
27884             this.el.setWidth("auto");
27885             tbl.setWidth("auto");
27886             if(Roo.isIE7 && Roo.isStrict){
27887                 var ib = this.el.child('button:first');
27888                 if(ib && ib.getWidth() > 20){
27889                     ib.clip();
27890                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27891                 }
27892             }
27893             if(this.minWidth){
27894                 if(this.hidden){
27895                     this.el.beginMeasure();
27896                 }
27897                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27898                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27899                 }
27900                 if(this.hidden){
27901                     this.el.endMeasure();
27902                 }
27903             }
27904             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27905         } 
27906     },
27907     /**
27908      * Sets this button's click handler
27909      * @param {Function} handler The function to call when the button is clicked
27910      * @param {Object} scope (optional) Scope for the function passed above
27911      */
27912     setHandler : function(handler, scope){
27913         this.handler = handler;
27914         this.scope = scope;  
27915     },
27916     
27917     /**
27918      * Sets this button's arrow click handler
27919      * @param {Function} handler The function to call when the arrow is clicked
27920      * @param {Object} scope (optional) Scope for the function passed above
27921      */
27922     setArrowHandler : function(handler, scope){
27923         this.arrowHandler = handler;
27924         this.scope = scope;  
27925     },
27926     
27927     /**
27928      * Focus the button
27929      */
27930     focus : function(){
27931         if(this.el){
27932             this.el.child("button:first").focus();
27933         }
27934     },
27935
27936     // private
27937     onClick : function(e){
27938         e.preventDefault();
27939         if(!this.disabled){
27940             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27941                 if(this.menu && !this.menu.isVisible()){
27942                     this.menu.show(this.el, this.menuAlign);
27943                 }
27944                 this.fireEvent("arrowclick", this, e);
27945                 if(this.arrowHandler){
27946                     this.arrowHandler.call(this.scope || this, this, e);
27947                 }
27948             }else{
27949                 this.fireEvent("click", this, e);
27950                 if(this.handler){
27951                     this.handler.call(this.scope || this, this, e);
27952                 }
27953             }
27954         }
27955     },
27956     // private
27957     onMouseDown : function(e){
27958         if(!this.disabled){
27959             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27960         }
27961     },
27962     // private
27963     onMouseUp : function(e){
27964         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27965     }   
27966 });
27967
27968
27969 // backwards compat
27970 Roo.MenuButton = Roo.SplitButton;/*
27971  * Based on:
27972  * Ext JS Library 1.1.1
27973  * Copyright(c) 2006-2007, Ext JS, LLC.
27974  *
27975  * Originally Released Under LGPL - original licence link has changed is not relivant.
27976  *
27977  * Fork - LGPL
27978  * <script type="text/javascript">
27979  */
27980
27981 /**
27982  * @class Roo.Toolbar
27983  * Basic Toolbar class.
27984  * @constructor
27985  * Creates a new Toolbar
27986  * @param {Object} container The config object
27987  */ 
27988 Roo.Toolbar = function(container, buttons, config)
27989 {
27990     /// old consturctor format still supported..
27991     if(container instanceof Array){ // omit the container for later rendering
27992         buttons = container;
27993         config = buttons;
27994         container = null;
27995     }
27996     if (typeof(container) == 'object' && container.xtype) {
27997         config = container;
27998         container = config.container;
27999         buttons = config.buttons || []; // not really - use items!!
28000     }
28001     var xitems = [];
28002     if (config && config.items) {
28003         xitems = config.items;
28004         delete config.items;
28005     }
28006     Roo.apply(this, config);
28007     this.buttons = buttons;
28008     
28009     if(container){
28010         this.render(container);
28011     }
28012     this.xitems = xitems;
28013     Roo.each(xitems, function(b) {
28014         this.add(b);
28015     }, this);
28016     
28017 };
28018
28019 Roo.Toolbar.prototype = {
28020     /**
28021      * @cfg {Array} items
28022      * array of button configs or elements to add (will be converted to a MixedCollection)
28023      */
28024     
28025     /**
28026      * @cfg {String/HTMLElement/Element} container
28027      * The id or element that will contain the toolbar
28028      */
28029     // private
28030     render : function(ct){
28031         this.el = Roo.get(ct);
28032         if(this.cls){
28033             this.el.addClass(this.cls);
28034         }
28035         // using a table allows for vertical alignment
28036         // 100% width is needed by Safari...
28037         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28038         this.tr = this.el.child("tr", true);
28039         var autoId = 0;
28040         this.items = new Roo.util.MixedCollection(false, function(o){
28041             return o.id || ("item" + (++autoId));
28042         });
28043         if(this.buttons){
28044             this.add.apply(this, this.buttons);
28045             delete this.buttons;
28046         }
28047     },
28048
28049     /**
28050      * Adds element(s) to the toolbar -- this function takes a variable number of 
28051      * arguments of mixed type and adds them to the toolbar.
28052      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28053      * <ul>
28054      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28055      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28056      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28057      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28058      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28059      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28060      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28061      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28062      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28063      * </ul>
28064      * @param {Mixed} arg2
28065      * @param {Mixed} etc.
28066      */
28067     add : function(){
28068         var a = arguments, l = a.length;
28069         for(var i = 0; i < l; i++){
28070             this._add(a[i]);
28071         }
28072     },
28073     // private..
28074     _add : function(el) {
28075         
28076         if (el.xtype) {
28077             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28078         }
28079         
28080         if (el.applyTo){ // some kind of form field
28081             return this.addField(el);
28082         } 
28083         if (el.render){ // some kind of Toolbar.Item
28084             return this.addItem(el);
28085         }
28086         if (typeof el == "string"){ // string
28087             if(el == "separator" || el == "-"){
28088                 return this.addSeparator();
28089             }
28090             if (el == " "){
28091                 return this.addSpacer();
28092             }
28093             if(el == "->"){
28094                 return this.addFill();
28095             }
28096             return this.addText(el);
28097             
28098         }
28099         if(el.tagName){ // element
28100             return this.addElement(el);
28101         }
28102         if(typeof el == "object"){ // must be button config?
28103             return this.addButton(el);
28104         }
28105         // and now what?!?!
28106         return false;
28107         
28108     },
28109     
28110     /**
28111      * Add an Xtype element
28112      * @param {Object} xtype Xtype Object
28113      * @return {Object} created Object
28114      */
28115     addxtype : function(e){
28116         return this.add(e);  
28117     },
28118     
28119     /**
28120      * Returns the Element for this toolbar.
28121      * @return {Roo.Element}
28122      */
28123     getEl : function(){
28124         return this.el;  
28125     },
28126     
28127     /**
28128      * Adds a separator
28129      * @return {Roo.Toolbar.Item} The separator item
28130      */
28131     addSeparator : function(){
28132         return this.addItem(new Roo.Toolbar.Separator());
28133     },
28134
28135     /**
28136      * Adds a spacer element
28137      * @return {Roo.Toolbar.Spacer} The spacer item
28138      */
28139     addSpacer : function(){
28140         return this.addItem(new Roo.Toolbar.Spacer());
28141     },
28142
28143     /**
28144      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28145      * @return {Roo.Toolbar.Fill} The fill item
28146      */
28147     addFill : function(){
28148         return this.addItem(new Roo.Toolbar.Fill());
28149     },
28150
28151     /**
28152      * Adds any standard HTML element to the toolbar
28153      * @param {String/HTMLElement/Element} el The element or id of the element to add
28154      * @return {Roo.Toolbar.Item} The element's item
28155      */
28156     addElement : function(el){
28157         return this.addItem(new Roo.Toolbar.Item(el));
28158     },
28159     /**
28160      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28161      * @type Roo.util.MixedCollection  
28162      */
28163     items : false,
28164      
28165     /**
28166      * Adds any Toolbar.Item or subclass
28167      * @param {Roo.Toolbar.Item} item
28168      * @return {Roo.Toolbar.Item} The item
28169      */
28170     addItem : function(item){
28171         var td = this.nextBlock();
28172         item.render(td);
28173         this.items.add(item);
28174         return item;
28175     },
28176     
28177     /**
28178      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28179      * @param {Object/Array} config A button config or array of configs
28180      * @return {Roo.Toolbar.Button/Array}
28181      */
28182     addButton : function(config){
28183         if(config instanceof Array){
28184             var buttons = [];
28185             for(var i = 0, len = config.length; i < len; i++) {
28186                 buttons.push(this.addButton(config[i]));
28187             }
28188             return buttons;
28189         }
28190         var b = config;
28191         if(!(config instanceof Roo.Toolbar.Button)){
28192             b = config.split ?
28193                 new Roo.Toolbar.SplitButton(config) :
28194                 new Roo.Toolbar.Button(config);
28195         }
28196         var td = this.nextBlock();
28197         b.render(td);
28198         this.items.add(b);
28199         return b;
28200     },
28201     
28202     /**
28203      * Adds text to the toolbar
28204      * @param {String} text The text to add
28205      * @return {Roo.Toolbar.Item} The element's item
28206      */
28207     addText : function(text){
28208         return this.addItem(new Roo.Toolbar.TextItem(text));
28209     },
28210     
28211     /**
28212      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28213      * @param {Number} index The index where the item is to be inserted
28214      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28215      * @return {Roo.Toolbar.Button/Item}
28216      */
28217     insertButton : function(index, item){
28218         if(item instanceof Array){
28219             var buttons = [];
28220             for(var i = 0, len = item.length; i < len; i++) {
28221                buttons.push(this.insertButton(index + i, item[i]));
28222             }
28223             return buttons;
28224         }
28225         if (!(item instanceof Roo.Toolbar.Button)){
28226            item = new Roo.Toolbar.Button(item);
28227         }
28228         var td = document.createElement("td");
28229         this.tr.insertBefore(td, this.tr.childNodes[index]);
28230         item.render(td);
28231         this.items.insert(index, item);
28232         return item;
28233     },
28234     
28235     /**
28236      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28237      * @param {Object} config
28238      * @return {Roo.Toolbar.Item} The element's item
28239      */
28240     addDom : function(config, returnEl){
28241         var td = this.nextBlock();
28242         Roo.DomHelper.overwrite(td, config);
28243         var ti = new Roo.Toolbar.Item(td.firstChild);
28244         ti.render(td);
28245         this.items.add(ti);
28246         return ti;
28247     },
28248
28249     /**
28250      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28251      * @type Roo.util.MixedCollection  
28252      */
28253     fields : false,
28254     
28255     /**
28256      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28257      * Note: the field should not have been rendered yet. For a field that has already been
28258      * rendered, use {@link #addElement}.
28259      * @param {Roo.form.Field} field
28260      * @return {Roo.ToolbarItem}
28261      */
28262      
28263       
28264     addField : function(field) {
28265         if (!this.fields) {
28266             var autoId = 0;
28267             this.fields = new Roo.util.MixedCollection(false, function(o){
28268                 return o.id || ("item" + (++autoId));
28269             });
28270
28271         }
28272         
28273         var td = this.nextBlock();
28274         field.render(td);
28275         var ti = new Roo.Toolbar.Item(td.firstChild);
28276         ti.render(td);
28277         this.items.add(ti);
28278         this.fields.add(field);
28279         return ti;
28280     },
28281     /**
28282      * Hide the toolbar
28283      * @method hide
28284      */
28285      
28286       
28287     hide : function()
28288     {
28289         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28290         this.el.child('div').hide();
28291     },
28292     /**
28293      * Show the toolbar
28294      * @method show
28295      */
28296     show : function()
28297     {
28298         this.el.child('div').show();
28299     },
28300       
28301     // private
28302     nextBlock : function(){
28303         var td = document.createElement("td");
28304         this.tr.appendChild(td);
28305         return td;
28306     },
28307
28308     // private
28309     destroy : function(){
28310         if(this.items){ // rendered?
28311             Roo.destroy.apply(Roo, this.items.items);
28312         }
28313         if(this.fields){ // rendered?
28314             Roo.destroy.apply(Roo, this.fields.items);
28315         }
28316         Roo.Element.uncache(this.el, this.tr);
28317     }
28318 };
28319
28320 /**
28321  * @class Roo.Toolbar.Item
28322  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28323  * @constructor
28324  * Creates a new Item
28325  * @param {HTMLElement} el 
28326  */
28327 Roo.Toolbar.Item = function(el){
28328     this.el = Roo.getDom(el);
28329     this.id = Roo.id(this.el);
28330     this.hidden = false;
28331 };
28332
28333 Roo.Toolbar.Item.prototype = {
28334     
28335     /**
28336      * Get this item's HTML Element
28337      * @return {HTMLElement}
28338      */
28339     getEl : function(){
28340        return this.el;  
28341     },
28342
28343     // private
28344     render : function(td){
28345         this.td = td;
28346         td.appendChild(this.el);
28347     },
28348     
28349     /**
28350      * Removes and destroys this item.
28351      */
28352     destroy : function(){
28353         this.td.parentNode.removeChild(this.td);
28354     },
28355     
28356     /**
28357      * Shows this item.
28358      */
28359     show: function(){
28360         this.hidden = false;
28361         this.td.style.display = "";
28362     },
28363     
28364     /**
28365      * Hides this item.
28366      */
28367     hide: function(){
28368         this.hidden = true;
28369         this.td.style.display = "none";
28370     },
28371     
28372     /**
28373      * Convenience function for boolean show/hide.
28374      * @param {Boolean} visible true to show/false to hide
28375      */
28376     setVisible: function(visible){
28377         if(visible) {
28378             this.show();
28379         }else{
28380             this.hide();
28381         }
28382     },
28383     
28384     /**
28385      * Try to focus this item.
28386      */
28387     focus : function(){
28388         Roo.fly(this.el).focus();
28389     },
28390     
28391     /**
28392      * Disables this item.
28393      */
28394     disable : function(){
28395         Roo.fly(this.td).addClass("x-item-disabled");
28396         this.disabled = true;
28397         this.el.disabled = true;
28398     },
28399     
28400     /**
28401      * Enables this item.
28402      */
28403     enable : function(){
28404         Roo.fly(this.td).removeClass("x-item-disabled");
28405         this.disabled = false;
28406         this.el.disabled = false;
28407     }
28408 };
28409
28410
28411 /**
28412  * @class Roo.Toolbar.Separator
28413  * @extends Roo.Toolbar.Item
28414  * A simple toolbar separator class
28415  * @constructor
28416  * Creates a new Separator
28417  */
28418 Roo.Toolbar.Separator = function(){
28419     var s = document.createElement("span");
28420     s.className = "ytb-sep";
28421     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28422 };
28423 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28424     enable:Roo.emptyFn,
28425     disable:Roo.emptyFn,
28426     focus:Roo.emptyFn
28427 });
28428
28429 /**
28430  * @class Roo.Toolbar.Spacer
28431  * @extends Roo.Toolbar.Item
28432  * A simple element that adds extra horizontal space to a toolbar.
28433  * @constructor
28434  * Creates a new Spacer
28435  */
28436 Roo.Toolbar.Spacer = function(){
28437     var s = document.createElement("div");
28438     s.className = "ytb-spacer";
28439     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28440 };
28441 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28442     enable:Roo.emptyFn,
28443     disable:Roo.emptyFn,
28444     focus:Roo.emptyFn
28445 });
28446
28447 /**
28448  * @class Roo.Toolbar.Fill
28449  * @extends Roo.Toolbar.Spacer
28450  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28451  * @constructor
28452  * Creates a new Spacer
28453  */
28454 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28455     // private
28456     render : function(td){
28457         td.style.width = '100%';
28458         Roo.Toolbar.Fill.superclass.render.call(this, td);
28459     }
28460 });
28461
28462 /**
28463  * @class Roo.Toolbar.TextItem
28464  * @extends Roo.Toolbar.Item
28465  * A simple class that renders text directly into a toolbar.
28466  * @constructor
28467  * Creates a new TextItem
28468  * @param {String} text
28469  */
28470 Roo.Toolbar.TextItem = function(text){
28471     if (typeof(text) == 'object') {
28472         text = text.text;
28473     }
28474     var s = document.createElement("span");
28475     s.className = "ytb-text";
28476     s.innerHTML = text;
28477     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28478 };
28479 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28480     enable:Roo.emptyFn,
28481     disable:Roo.emptyFn,
28482     focus:Roo.emptyFn
28483 });
28484
28485 /**
28486  * @class Roo.Toolbar.Button
28487  * @extends Roo.Button
28488  * A button that renders into a toolbar.
28489  * @constructor
28490  * Creates a new Button
28491  * @param {Object} config A standard {@link Roo.Button} config object
28492  */
28493 Roo.Toolbar.Button = function(config){
28494     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28495 };
28496 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28497     render : function(td){
28498         this.td = td;
28499         Roo.Toolbar.Button.superclass.render.call(this, td);
28500     },
28501     
28502     /**
28503      * Removes and destroys this button
28504      */
28505     destroy : function(){
28506         Roo.Toolbar.Button.superclass.destroy.call(this);
28507         this.td.parentNode.removeChild(this.td);
28508     },
28509     
28510     /**
28511      * Shows this button
28512      */
28513     show: function(){
28514         this.hidden = false;
28515         this.td.style.display = "";
28516     },
28517     
28518     /**
28519      * Hides this button
28520      */
28521     hide: function(){
28522         this.hidden = true;
28523         this.td.style.display = "none";
28524     },
28525
28526     /**
28527      * Disables this item
28528      */
28529     disable : function(){
28530         Roo.fly(this.td).addClass("x-item-disabled");
28531         this.disabled = true;
28532     },
28533
28534     /**
28535      * Enables this item
28536      */
28537     enable : function(){
28538         Roo.fly(this.td).removeClass("x-item-disabled");
28539         this.disabled = false;
28540     }
28541 });
28542 // backwards compat
28543 Roo.ToolbarButton = Roo.Toolbar.Button;
28544
28545 /**
28546  * @class Roo.Toolbar.SplitButton
28547  * @extends Roo.SplitButton
28548  * A menu button that renders into a toolbar.
28549  * @constructor
28550  * Creates a new SplitButton
28551  * @param {Object} config A standard {@link Roo.SplitButton} config object
28552  */
28553 Roo.Toolbar.SplitButton = function(config){
28554     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28555 };
28556 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28557     render : function(td){
28558         this.td = td;
28559         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28560     },
28561     
28562     /**
28563      * Removes and destroys this button
28564      */
28565     destroy : function(){
28566         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28567         this.td.parentNode.removeChild(this.td);
28568     },
28569     
28570     /**
28571      * Shows this button
28572      */
28573     show: function(){
28574         this.hidden = false;
28575         this.td.style.display = "";
28576     },
28577     
28578     /**
28579      * Hides this button
28580      */
28581     hide: function(){
28582         this.hidden = true;
28583         this.td.style.display = "none";
28584     }
28585 });
28586
28587 // backwards compat
28588 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28589  * Based on:
28590  * Ext JS Library 1.1.1
28591  * Copyright(c) 2006-2007, Ext JS, LLC.
28592  *
28593  * Originally Released Under LGPL - original licence link has changed is not relivant.
28594  *
28595  * Fork - LGPL
28596  * <script type="text/javascript">
28597  */
28598  
28599 /**
28600  * @class Roo.PagingToolbar
28601  * @extends Roo.Toolbar
28602  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28603  * @constructor
28604  * Create a new PagingToolbar
28605  * @param {Object} config The config object
28606  */
28607 Roo.PagingToolbar = function(el, ds, config)
28608 {
28609     // old args format still supported... - xtype is prefered..
28610     if (typeof(el) == 'object' && el.xtype) {
28611         // created from xtype...
28612         config = el;
28613         ds = el.dataSource;
28614         el = config.container;
28615     }
28616     var items = [];
28617     if (config.items) {
28618         items = config.items;
28619         config.items = [];
28620     }
28621     
28622     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28623     this.ds = ds;
28624     this.cursor = 0;
28625     this.renderButtons(this.el);
28626     this.bind(ds);
28627     
28628     // supprot items array.
28629    
28630     Roo.each(items, function(e) {
28631         this.add(Roo.factory(e));
28632     },this);
28633     
28634 };
28635
28636 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28637     /**
28638      * @cfg {Roo.data.Store} dataSource
28639      * The underlying data store providing the paged data
28640      */
28641     /**
28642      * @cfg {String/HTMLElement/Element} container
28643      * container The id or element that will contain the toolbar
28644      */
28645     /**
28646      * @cfg {Boolean} displayInfo
28647      * True to display the displayMsg (defaults to false)
28648      */
28649     /**
28650      * @cfg {Number} pageSize
28651      * The number of records to display per page (defaults to 20)
28652      */
28653     pageSize: 20,
28654     /**
28655      * @cfg {String} displayMsg
28656      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28657      */
28658     displayMsg : 'Displaying {0} - {1} of {2}',
28659     /**
28660      * @cfg {String} emptyMsg
28661      * The message to display when no records are found (defaults to "No data to display")
28662      */
28663     emptyMsg : 'No data to display',
28664     /**
28665      * Customizable piece of the default paging text (defaults to "Page")
28666      * @type String
28667      */
28668     beforePageText : "Page",
28669     /**
28670      * Customizable piece of the default paging text (defaults to "of %0")
28671      * @type String
28672      */
28673     afterPageText : "of {0}",
28674     /**
28675      * Customizable piece of the default paging text (defaults to "First Page")
28676      * @type String
28677      */
28678     firstText : "First Page",
28679     /**
28680      * Customizable piece of the default paging text (defaults to "Previous Page")
28681      * @type String
28682      */
28683     prevText : "Previous Page",
28684     /**
28685      * Customizable piece of the default paging text (defaults to "Next Page")
28686      * @type String
28687      */
28688     nextText : "Next Page",
28689     /**
28690      * Customizable piece of the default paging text (defaults to "Last Page")
28691      * @type String
28692      */
28693     lastText : "Last Page",
28694     /**
28695      * Customizable piece of the default paging text (defaults to "Refresh")
28696      * @type String
28697      */
28698     refreshText : "Refresh",
28699
28700     // private
28701     renderButtons : function(el){
28702         Roo.PagingToolbar.superclass.render.call(this, el);
28703         this.first = this.addButton({
28704             tooltip: this.firstText,
28705             cls: "x-btn-icon x-grid-page-first",
28706             disabled: true,
28707             handler: this.onClick.createDelegate(this, ["first"])
28708         });
28709         this.prev = this.addButton({
28710             tooltip: this.prevText,
28711             cls: "x-btn-icon x-grid-page-prev",
28712             disabled: true,
28713             handler: this.onClick.createDelegate(this, ["prev"])
28714         });
28715         //this.addSeparator();
28716         this.add(this.beforePageText);
28717         this.field = Roo.get(this.addDom({
28718            tag: "input",
28719            type: "text",
28720            size: "3",
28721            value: "1",
28722            cls: "x-grid-page-number"
28723         }).el);
28724         this.field.on("keydown", this.onPagingKeydown, this);
28725         this.field.on("focus", function(){this.dom.select();});
28726         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28727         this.field.setHeight(18);
28728         //this.addSeparator();
28729         this.next = this.addButton({
28730             tooltip: this.nextText,
28731             cls: "x-btn-icon x-grid-page-next",
28732             disabled: true,
28733             handler: this.onClick.createDelegate(this, ["next"])
28734         });
28735         this.last = this.addButton({
28736             tooltip: this.lastText,
28737             cls: "x-btn-icon x-grid-page-last",
28738             disabled: true,
28739             handler: this.onClick.createDelegate(this, ["last"])
28740         });
28741         //this.addSeparator();
28742         this.loading = this.addButton({
28743             tooltip: this.refreshText,
28744             cls: "x-btn-icon x-grid-loading",
28745             handler: this.onClick.createDelegate(this, ["refresh"])
28746         });
28747
28748         if(this.displayInfo){
28749             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28750         }
28751     },
28752
28753     // private
28754     updateInfo : function(){
28755         if(this.displayEl){
28756             var count = this.ds.getCount();
28757             var msg = count == 0 ?
28758                 this.emptyMsg :
28759                 String.format(
28760                     this.displayMsg,
28761                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28762                 );
28763             this.displayEl.update(msg);
28764         }
28765     },
28766
28767     // private
28768     onLoad : function(ds, r, o){
28769        this.cursor = o.params ? o.params.start : 0;
28770        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28771
28772        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28773        this.field.dom.value = ap;
28774        this.first.setDisabled(ap == 1);
28775        this.prev.setDisabled(ap == 1);
28776        this.next.setDisabled(ap == ps);
28777        this.last.setDisabled(ap == ps);
28778        this.loading.enable();
28779        this.updateInfo();
28780     },
28781
28782     // private
28783     getPageData : function(){
28784         var total = this.ds.getTotalCount();
28785         return {
28786             total : total,
28787             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28788             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28789         };
28790     },
28791
28792     // private
28793     onLoadError : function(){
28794         this.loading.enable();
28795     },
28796
28797     // private
28798     onPagingKeydown : function(e){
28799         var k = e.getKey();
28800         var d = this.getPageData();
28801         if(k == e.RETURN){
28802             var v = this.field.dom.value, pageNum;
28803             if(!v || isNaN(pageNum = parseInt(v, 10))){
28804                 this.field.dom.value = d.activePage;
28805                 return;
28806             }
28807             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28808             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28809             e.stopEvent();
28810         }
28811         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))
28812         {
28813           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28814           this.field.dom.value = pageNum;
28815           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28816           e.stopEvent();
28817         }
28818         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28819         {
28820           var v = this.field.dom.value, pageNum; 
28821           var increment = (e.shiftKey) ? 10 : 1;
28822           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28823             increment *= -1;
28824           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28825             this.field.dom.value = d.activePage;
28826             return;
28827           }
28828           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28829           {
28830             this.field.dom.value = parseInt(v, 10) + increment;
28831             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28832             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28833           }
28834           e.stopEvent();
28835         }
28836     },
28837
28838     // private
28839     beforeLoad : function(){
28840         if(this.loading){
28841             this.loading.disable();
28842         }
28843     },
28844
28845     // private
28846     onClick : function(which){
28847         var ds = this.ds;
28848         switch(which){
28849             case "first":
28850                 ds.load({params:{start: 0, limit: this.pageSize}});
28851             break;
28852             case "prev":
28853                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28854             break;
28855             case "next":
28856                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28857             break;
28858             case "last":
28859                 var total = ds.getTotalCount();
28860                 var extra = total % this.pageSize;
28861                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28862                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28863             break;
28864             case "refresh":
28865                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28866             break;
28867         }
28868     },
28869
28870     /**
28871      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28872      * @param {Roo.data.Store} store The data store to unbind
28873      */
28874     unbind : function(ds){
28875         ds.un("beforeload", this.beforeLoad, this);
28876         ds.un("load", this.onLoad, this);
28877         ds.un("loadexception", this.onLoadError, this);
28878         ds.un("remove", this.updateInfo, this);
28879         ds.un("add", this.updateInfo, this);
28880         this.ds = undefined;
28881     },
28882
28883     /**
28884      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28885      * @param {Roo.data.Store} store The data store to bind
28886      */
28887     bind : function(ds){
28888         ds.on("beforeload", this.beforeLoad, this);
28889         ds.on("load", this.onLoad, this);
28890         ds.on("loadexception", this.onLoadError, this);
28891         ds.on("remove", this.updateInfo, this);
28892         ds.on("add", this.updateInfo, this);
28893         this.ds = ds;
28894     }
28895 });/*
28896  * Based on:
28897  * Ext JS Library 1.1.1
28898  * Copyright(c) 2006-2007, Ext JS, LLC.
28899  *
28900  * Originally Released Under LGPL - original licence link has changed is not relivant.
28901  *
28902  * Fork - LGPL
28903  * <script type="text/javascript">
28904  */
28905
28906 /**
28907  * @class Roo.Resizable
28908  * @extends Roo.util.Observable
28909  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28910  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28911  * 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
28912  * the element will be wrapped for you automatically.</p>
28913  * <p>Here is the list of valid resize handles:</p>
28914  * <pre>
28915 Value   Description
28916 ------  -------------------
28917  'n'     north
28918  's'     south
28919  'e'     east
28920  'w'     west
28921  'nw'    northwest
28922  'sw'    southwest
28923  'se'    southeast
28924  'ne'    northeast
28925  'hd'    horizontal drag
28926  'all'   all
28927 </pre>
28928  * <p>Here's an example showing the creation of a typical Resizable:</p>
28929  * <pre><code>
28930 var resizer = new Roo.Resizable("element-id", {
28931     handles: 'all',
28932     minWidth: 200,
28933     minHeight: 100,
28934     maxWidth: 500,
28935     maxHeight: 400,
28936     pinned: true
28937 });
28938 resizer.on("resize", myHandler);
28939 </code></pre>
28940  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28941  * resizer.east.setDisplayed(false);</p>
28942  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28943  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28944  * resize operation's new size (defaults to [0, 0])
28945  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28946  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28947  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28948  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28949  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28950  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28951  * @cfg {Number} width The width of the element in pixels (defaults to null)
28952  * @cfg {Number} height The height of the element in pixels (defaults to null)
28953  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28954  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28955  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28956  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28957  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28958  * in favor of the handles config option (defaults to false)
28959  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28960  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28961  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28962  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28963  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28964  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28965  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28966  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28967  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28968  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28969  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28970  * @constructor
28971  * Create a new resizable component
28972  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28973  * @param {Object} config configuration options
28974   */
28975 Roo.Resizable = function(el, config)
28976 {
28977     this.el = Roo.get(el);
28978
28979     if(config && config.wrap){
28980         config.resizeChild = this.el;
28981         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28982         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28983         this.el.setStyle("overflow", "hidden");
28984         this.el.setPositioning(config.resizeChild.getPositioning());
28985         config.resizeChild.clearPositioning();
28986         if(!config.width || !config.height){
28987             var csize = config.resizeChild.getSize();
28988             this.el.setSize(csize.width, csize.height);
28989         }
28990         if(config.pinned && !config.adjustments){
28991             config.adjustments = "auto";
28992         }
28993     }
28994
28995     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28996     this.proxy.unselectable();
28997     this.proxy.enableDisplayMode('block');
28998
28999     Roo.apply(this, config);
29000
29001     if(this.pinned){
29002         this.disableTrackOver = true;
29003         this.el.addClass("x-resizable-pinned");
29004     }
29005     // if the element isn't positioned, make it relative
29006     var position = this.el.getStyle("position");
29007     if(position != "absolute" && position != "fixed"){
29008         this.el.setStyle("position", "relative");
29009     }
29010     if(!this.handles){ // no handles passed, must be legacy style
29011         this.handles = 's,e,se';
29012         if(this.multiDirectional){
29013             this.handles += ',n,w';
29014         }
29015     }
29016     if(this.handles == "all"){
29017         this.handles = "n s e w ne nw se sw";
29018     }
29019     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29020     var ps = Roo.Resizable.positions;
29021     for(var i = 0, len = hs.length; i < len; i++){
29022         if(hs[i] && ps[hs[i]]){
29023             var pos = ps[hs[i]];
29024             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29025         }
29026     }
29027     // legacy
29028     this.corner = this.southeast;
29029     
29030     // updateBox = the box can move..
29031     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29032         this.updateBox = true;
29033     }
29034
29035     this.activeHandle = null;
29036
29037     if(this.resizeChild){
29038         if(typeof this.resizeChild == "boolean"){
29039             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29040         }else{
29041             this.resizeChild = Roo.get(this.resizeChild, true);
29042         }
29043     }
29044     
29045     if(this.adjustments == "auto"){
29046         var rc = this.resizeChild;
29047         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29048         if(rc && (hw || hn)){
29049             rc.position("relative");
29050             rc.setLeft(hw ? hw.el.getWidth() : 0);
29051             rc.setTop(hn ? hn.el.getHeight() : 0);
29052         }
29053         this.adjustments = [
29054             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29055             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29056         ];
29057     }
29058
29059     if(this.draggable){
29060         this.dd = this.dynamic ?
29061             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29062         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29063     }
29064
29065     // public events
29066     this.addEvents({
29067         /**
29068          * @event beforeresize
29069          * Fired before resize is allowed. Set enabled to false to cancel resize.
29070          * @param {Roo.Resizable} this
29071          * @param {Roo.EventObject} e The mousedown event
29072          */
29073         "beforeresize" : true,
29074         /**
29075          * @event resizing
29076          * Fired a resizing.
29077          * @param {Roo.Resizable} this
29078          * @param {Number} x The new x position
29079          * @param {Number} y The new y position
29080          * @param {Number} w The new w width
29081          * @param {Number} h The new h hight
29082          * @param {Roo.EventObject} e The mouseup event
29083          */
29084         "resizing" : true,
29085         /**
29086          * @event resize
29087          * Fired after a resize.
29088          * @param {Roo.Resizable} this
29089          * @param {Number} width The new width
29090          * @param {Number} height The new height
29091          * @param {Roo.EventObject} e The mouseup event
29092          */
29093         "resize" : true
29094     });
29095
29096     if(this.width !== null && this.height !== null){
29097         this.resizeTo(this.width, this.height);
29098     }else{
29099         this.updateChildSize();
29100     }
29101     if(Roo.isIE){
29102         this.el.dom.style.zoom = 1;
29103     }
29104     Roo.Resizable.superclass.constructor.call(this);
29105 };
29106
29107 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29108         resizeChild : false,
29109         adjustments : [0, 0],
29110         minWidth : 5,
29111         minHeight : 5,
29112         maxWidth : 10000,
29113         maxHeight : 10000,
29114         enabled : true,
29115         animate : false,
29116         duration : .35,
29117         dynamic : false,
29118         handles : false,
29119         multiDirectional : false,
29120         disableTrackOver : false,
29121         easing : 'easeOutStrong',
29122         widthIncrement : 0,
29123         heightIncrement : 0,
29124         pinned : false,
29125         width : null,
29126         height : null,
29127         preserveRatio : false,
29128         transparent: false,
29129         minX: 0,
29130         minY: 0,
29131         draggable: false,
29132
29133         /**
29134          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29135          */
29136         constrainTo: undefined,
29137         /**
29138          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29139          */
29140         resizeRegion: undefined,
29141
29142
29143     /**
29144      * Perform a manual resize
29145      * @param {Number} width
29146      * @param {Number} height
29147      */
29148     resizeTo : function(width, height){
29149         this.el.setSize(width, height);
29150         this.updateChildSize();
29151         this.fireEvent("resize", this, width, height, null);
29152     },
29153
29154     // private
29155     startSizing : function(e, handle){
29156         this.fireEvent("beforeresize", this, e);
29157         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29158
29159             if(!this.overlay){
29160                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29161                 this.overlay.unselectable();
29162                 this.overlay.enableDisplayMode("block");
29163                 this.overlay.on("mousemove", this.onMouseMove, this);
29164                 this.overlay.on("mouseup", this.onMouseUp, this);
29165             }
29166             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29167
29168             this.resizing = true;
29169             this.startBox = this.el.getBox();
29170             this.startPoint = e.getXY();
29171             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29172                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29173
29174             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29175             this.overlay.show();
29176
29177             if(this.constrainTo) {
29178                 var ct = Roo.get(this.constrainTo);
29179                 this.resizeRegion = ct.getRegion().adjust(
29180                     ct.getFrameWidth('t'),
29181                     ct.getFrameWidth('l'),
29182                     -ct.getFrameWidth('b'),
29183                     -ct.getFrameWidth('r')
29184                 );
29185             }
29186
29187             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29188             this.proxy.show();
29189             this.proxy.setBox(this.startBox);
29190             if(!this.dynamic){
29191                 this.proxy.setStyle('visibility', 'visible');
29192             }
29193         }
29194     },
29195
29196     // private
29197     onMouseDown : function(handle, e){
29198         if(this.enabled){
29199             e.stopEvent();
29200             this.activeHandle = handle;
29201             this.startSizing(e, handle);
29202         }
29203     },
29204
29205     // private
29206     onMouseUp : function(e){
29207         var size = this.resizeElement();
29208         this.resizing = false;
29209         this.handleOut();
29210         this.overlay.hide();
29211         this.proxy.hide();
29212         this.fireEvent("resize", this, size.width, size.height, e);
29213     },
29214
29215     // private
29216     updateChildSize : function(){
29217         
29218         if(this.resizeChild){
29219             var el = this.el;
29220             var child = this.resizeChild;
29221             var adj = this.adjustments;
29222             if(el.dom.offsetWidth){
29223                 var b = el.getSize(true);
29224                 child.setSize(b.width+adj[0], b.height+adj[1]);
29225             }
29226             // Second call here for IE
29227             // The first call enables instant resizing and
29228             // the second call corrects scroll bars if they
29229             // exist
29230             if(Roo.isIE){
29231                 setTimeout(function(){
29232                     if(el.dom.offsetWidth){
29233                         var b = el.getSize(true);
29234                         child.setSize(b.width+adj[0], b.height+adj[1]);
29235                     }
29236                 }, 10);
29237             }
29238         }
29239     },
29240
29241     // private
29242     snap : function(value, inc, min){
29243         if(!inc || !value) return value;
29244         var newValue = value;
29245         var m = value % inc;
29246         if(m > 0){
29247             if(m > (inc/2)){
29248                 newValue = value + (inc-m);
29249             }else{
29250                 newValue = value - m;
29251             }
29252         }
29253         return Math.max(min, newValue);
29254     },
29255
29256     // private
29257     resizeElement : function(){
29258         var box = this.proxy.getBox();
29259         if(this.updateBox){
29260             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29261         }else{
29262             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29263         }
29264         this.updateChildSize();
29265         if(!this.dynamic){
29266             this.proxy.hide();
29267         }
29268         return box;
29269     },
29270
29271     // private
29272     constrain : function(v, diff, m, mx){
29273         if(v - diff < m){
29274             diff = v - m;
29275         }else if(v - diff > mx){
29276             diff = mx - v;
29277         }
29278         return diff;
29279     },
29280
29281     // private
29282     onMouseMove : function(e){
29283         
29284         if(this.enabled){
29285             try{// try catch so if something goes wrong the user doesn't get hung
29286
29287             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29288                 return;
29289             }
29290
29291             //var curXY = this.startPoint;
29292             var curSize = this.curSize || this.startBox;
29293             var x = this.startBox.x, y = this.startBox.y;
29294             var ox = x, oy = y;
29295             var w = curSize.width, h = curSize.height;
29296             var ow = w, oh = h;
29297             var mw = this.minWidth, mh = this.minHeight;
29298             var mxw = this.maxWidth, mxh = this.maxHeight;
29299             var wi = this.widthIncrement;
29300             var hi = this.heightIncrement;
29301
29302             var eventXY = e.getXY();
29303             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29304             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29305
29306             var pos = this.activeHandle.position;
29307
29308             switch(pos){
29309                 case "east":
29310                     w += diffX;
29311                     w = Math.min(Math.max(mw, w), mxw);
29312                     break;
29313              
29314                 case "south":
29315                     h += diffY;
29316                     h = Math.min(Math.max(mh, h), mxh);
29317                     break;
29318                 case "southeast":
29319                     w += diffX;
29320                     h += diffY;
29321                     w = Math.min(Math.max(mw, w), mxw);
29322                     h = Math.min(Math.max(mh, h), mxh);
29323                     break;
29324                 case "north":
29325                     diffY = this.constrain(h, diffY, mh, mxh);
29326                     y += diffY;
29327                     h -= diffY;
29328                     break;
29329                 case "hdrag":
29330                     
29331                     if (wi) {
29332                         var adiffX = Math.abs(diffX);
29333                         var sub = (adiffX % wi); // how much 
29334                         if (sub > (wi/2)) { // far enough to snap
29335                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29336                         } else {
29337                             // remove difference.. 
29338                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29339                         }
29340                     }
29341                     x += diffX;
29342                     x = Math.max(this.minX, x);
29343                     break;
29344                 case "west":
29345                     diffX = this.constrain(w, diffX, mw, mxw);
29346                     x += diffX;
29347                     w -= diffX;
29348                     break;
29349                 case "northeast":
29350                     w += diffX;
29351                     w = Math.min(Math.max(mw, w), mxw);
29352                     diffY = this.constrain(h, diffY, mh, mxh);
29353                     y += diffY;
29354                     h -= diffY;
29355                     break;
29356                 case "northwest":
29357                     diffX = this.constrain(w, diffX, mw, mxw);
29358                     diffY = this.constrain(h, diffY, mh, mxh);
29359                     y += diffY;
29360                     h -= diffY;
29361                     x += diffX;
29362                     w -= diffX;
29363                     break;
29364                case "southwest":
29365                     diffX = this.constrain(w, diffX, mw, mxw);
29366                     h += diffY;
29367                     h = Math.min(Math.max(mh, h), mxh);
29368                     x += diffX;
29369                     w -= diffX;
29370                     break;
29371             }
29372
29373             var sw = this.snap(w, wi, mw);
29374             var sh = this.snap(h, hi, mh);
29375             if(sw != w || sh != h){
29376                 switch(pos){
29377                     case "northeast":
29378                         y -= sh - h;
29379                     break;
29380                     case "north":
29381                         y -= sh - h;
29382                         break;
29383                     case "southwest":
29384                         x -= sw - w;
29385                     break;
29386                     case "west":
29387                         x -= sw - w;
29388                         break;
29389                     case "northwest":
29390                         x -= sw - w;
29391                         y -= sh - h;
29392                     break;
29393                 }
29394                 w = sw;
29395                 h = sh;
29396             }
29397
29398             if(this.preserveRatio){
29399                 switch(pos){
29400                     case "southeast":
29401                     case "east":
29402                         h = oh * (w/ow);
29403                         h = Math.min(Math.max(mh, h), mxh);
29404                         w = ow * (h/oh);
29405                        break;
29406                     case "south":
29407                         w = ow * (h/oh);
29408                         w = Math.min(Math.max(mw, w), mxw);
29409                         h = oh * (w/ow);
29410                         break;
29411                     case "northeast":
29412                         w = ow * (h/oh);
29413                         w = Math.min(Math.max(mw, w), mxw);
29414                         h = oh * (w/ow);
29415                     break;
29416                     case "north":
29417                         var tw = w;
29418                         w = ow * (h/oh);
29419                         w = Math.min(Math.max(mw, w), mxw);
29420                         h = oh * (w/ow);
29421                         x += (tw - w) / 2;
29422                         break;
29423                     case "southwest":
29424                         h = oh * (w/ow);
29425                         h = Math.min(Math.max(mh, h), mxh);
29426                         var tw = w;
29427                         w = ow * (h/oh);
29428                         x += tw - w;
29429                         break;
29430                     case "west":
29431                         var th = h;
29432                         h = oh * (w/ow);
29433                         h = Math.min(Math.max(mh, h), mxh);
29434                         y += (th - h) / 2;
29435                         var tw = w;
29436                         w = ow * (h/oh);
29437                         x += tw - w;
29438                        break;
29439                     case "northwest":
29440                         var tw = w;
29441                         var th = h;
29442                         h = oh * (w/ow);
29443                         h = Math.min(Math.max(mh, h), mxh);
29444                         w = ow * (h/oh);
29445                         y += th - h;
29446                         x += tw - w;
29447                        break;
29448
29449                 }
29450             }
29451             if (pos == 'hdrag') {
29452                 w = ow;
29453             }
29454             this.proxy.setBounds(x, y, w, h);
29455             if(this.dynamic){
29456                 this.resizeElement();
29457             }
29458             }catch(e){}
29459         }
29460         this.fireEvent("resizing", this, x, y, w, h, e);
29461     },
29462
29463     // private
29464     handleOver : function(){
29465         if(this.enabled){
29466             this.el.addClass("x-resizable-over");
29467         }
29468     },
29469
29470     // private
29471     handleOut : function(){
29472         if(!this.resizing){
29473             this.el.removeClass("x-resizable-over");
29474         }
29475     },
29476
29477     /**
29478      * Returns the element this component is bound to.
29479      * @return {Roo.Element}
29480      */
29481     getEl : function(){
29482         return this.el;
29483     },
29484
29485     /**
29486      * Returns the resizeChild element (or null).
29487      * @return {Roo.Element}
29488      */
29489     getResizeChild : function(){
29490         return this.resizeChild;
29491     },
29492     groupHandler : function()
29493     {
29494         
29495     },
29496     /**
29497      * Destroys this resizable. If the element was wrapped and
29498      * removeEl is not true then the element remains.
29499      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29500      */
29501     destroy : function(removeEl){
29502         this.proxy.remove();
29503         if(this.overlay){
29504             this.overlay.removeAllListeners();
29505             this.overlay.remove();
29506         }
29507         var ps = Roo.Resizable.positions;
29508         for(var k in ps){
29509             if(typeof ps[k] != "function" && this[ps[k]]){
29510                 var h = this[ps[k]];
29511                 h.el.removeAllListeners();
29512                 h.el.remove();
29513             }
29514         }
29515         if(removeEl){
29516             this.el.update("");
29517             this.el.remove();
29518         }
29519     }
29520 });
29521
29522 // private
29523 // hash to map config positions to true positions
29524 Roo.Resizable.positions = {
29525     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29526     hd: "hdrag"
29527 };
29528
29529 // private
29530 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29531     if(!this.tpl){
29532         // only initialize the template if resizable is used
29533         var tpl = Roo.DomHelper.createTemplate(
29534             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29535         );
29536         tpl.compile();
29537         Roo.Resizable.Handle.prototype.tpl = tpl;
29538     }
29539     this.position = pos;
29540     this.rz = rz;
29541     // show north drag fro topdra
29542     var handlepos = pos == 'hdrag' ? 'north' : pos;
29543     
29544     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29545     if (pos == 'hdrag') {
29546         this.el.setStyle('cursor', 'pointer');
29547     }
29548     this.el.unselectable();
29549     if(transparent){
29550         this.el.setOpacity(0);
29551     }
29552     this.el.on("mousedown", this.onMouseDown, this);
29553     if(!disableTrackOver){
29554         this.el.on("mouseover", this.onMouseOver, this);
29555         this.el.on("mouseout", this.onMouseOut, this);
29556     }
29557 };
29558
29559 // private
29560 Roo.Resizable.Handle.prototype = {
29561     afterResize : function(rz){
29562         // do nothing
29563     },
29564     // private
29565     onMouseDown : function(e){
29566         this.rz.onMouseDown(this, e);
29567     },
29568     // private
29569     onMouseOver : function(e){
29570         this.rz.handleOver(this, e);
29571     },
29572     // private
29573     onMouseOut : function(e){
29574         this.rz.handleOut(this, e);
29575     }
29576 };/*
29577  * Based on:
29578  * Ext JS Library 1.1.1
29579  * Copyright(c) 2006-2007, Ext JS, LLC.
29580  *
29581  * Originally Released Under LGPL - original licence link has changed is not relivant.
29582  *
29583  * Fork - LGPL
29584  * <script type="text/javascript">
29585  */
29586
29587 /**
29588  * @class Roo.Editor
29589  * @extends Roo.Component
29590  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29591  * @constructor
29592  * Create a new Editor
29593  * @param {Roo.form.Field} field The Field object (or descendant)
29594  * @param {Object} config The config object
29595  */
29596 Roo.Editor = function(field, config){
29597     Roo.Editor.superclass.constructor.call(this, config);
29598     this.field = field;
29599     this.addEvents({
29600         /**
29601              * @event beforestartedit
29602              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29603              * false from the handler of this event.
29604              * @param {Editor} this
29605              * @param {Roo.Element} boundEl The underlying element bound to this editor
29606              * @param {Mixed} value The field value being set
29607              */
29608         "beforestartedit" : true,
29609         /**
29610              * @event startedit
29611              * Fires when this editor is displayed
29612              * @param {Roo.Element} boundEl The underlying element bound to this editor
29613              * @param {Mixed} value The starting field value
29614              */
29615         "startedit" : true,
29616         /**
29617              * @event beforecomplete
29618              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29619              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29620              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29621              * event will not fire since no edit actually occurred.
29622              * @param {Editor} this
29623              * @param {Mixed} value The current field value
29624              * @param {Mixed} startValue The original field value
29625              */
29626         "beforecomplete" : true,
29627         /**
29628              * @event complete
29629              * Fires after editing is complete and any changed value has been written to the underlying field.
29630              * @param {Editor} this
29631              * @param {Mixed} value The current field value
29632              * @param {Mixed} startValue The original field value
29633              */
29634         "complete" : true,
29635         /**
29636          * @event specialkey
29637          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29638          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29639          * @param {Roo.form.Field} this
29640          * @param {Roo.EventObject} e The event object
29641          */
29642         "specialkey" : true
29643     });
29644 };
29645
29646 Roo.extend(Roo.Editor, Roo.Component, {
29647     /**
29648      * @cfg {Boolean/String} autosize
29649      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29650      * or "height" to adopt the height only (defaults to false)
29651      */
29652     /**
29653      * @cfg {Boolean} revertInvalid
29654      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29655      * validation fails (defaults to true)
29656      */
29657     /**
29658      * @cfg {Boolean} ignoreNoChange
29659      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29660      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29661      * will never be ignored.
29662      */
29663     /**
29664      * @cfg {Boolean} hideEl
29665      * False to keep the bound element visible while the editor is displayed (defaults to true)
29666      */
29667     /**
29668      * @cfg {Mixed} value
29669      * The data value of the underlying field (defaults to "")
29670      */
29671     value : "",
29672     /**
29673      * @cfg {String} alignment
29674      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29675      */
29676     alignment: "c-c?",
29677     /**
29678      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29679      * for bottom-right shadow (defaults to "frame")
29680      */
29681     shadow : "frame",
29682     /**
29683      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29684      */
29685     constrain : false,
29686     /**
29687      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29688      */
29689     completeOnEnter : false,
29690     /**
29691      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29692      */
29693     cancelOnEsc : false,
29694     /**
29695      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29696      */
29697     updateEl : false,
29698
29699     // private
29700     onRender : function(ct, position){
29701         this.el = new Roo.Layer({
29702             shadow: this.shadow,
29703             cls: "x-editor",
29704             parentEl : ct,
29705             shim : this.shim,
29706             shadowOffset:4,
29707             id: this.id,
29708             constrain: this.constrain
29709         });
29710         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29711         if(this.field.msgTarget != 'title'){
29712             this.field.msgTarget = 'qtip';
29713         }
29714         this.field.render(this.el);
29715         if(Roo.isGecko){
29716             this.field.el.dom.setAttribute('autocomplete', 'off');
29717         }
29718         this.field.on("specialkey", this.onSpecialKey, this);
29719         if(this.swallowKeys){
29720             this.field.el.swallowEvent(['keydown','keypress']);
29721         }
29722         this.field.show();
29723         this.field.on("blur", this.onBlur, this);
29724         if(this.field.grow){
29725             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29726         }
29727     },
29728
29729     onSpecialKey : function(field, e)
29730     {
29731         //Roo.log('editor onSpecialKey');
29732         if(this.completeOnEnter && e.getKey() == e.ENTER){
29733             e.stopEvent();
29734             this.completeEdit();
29735             return;
29736         }
29737         // do not fire special key otherwise it might hide close the editor...
29738         if(e.getKey() == e.ENTER){    
29739             return;
29740         }
29741         if(this.cancelOnEsc && e.getKey() == e.ESC){
29742             this.cancelEdit();
29743             return;
29744         } 
29745         this.fireEvent('specialkey', field, e);
29746     
29747     },
29748
29749     /**
29750      * Starts the editing process and shows the editor.
29751      * @param {String/HTMLElement/Element} el The element to edit
29752      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29753       * to the innerHTML of el.
29754      */
29755     startEdit : function(el, value){
29756         if(this.editing){
29757             this.completeEdit();
29758         }
29759         this.boundEl = Roo.get(el);
29760         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29761         if(!this.rendered){
29762             this.render(this.parentEl || document.body);
29763         }
29764         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29765             return;
29766         }
29767         this.startValue = v;
29768         this.field.setValue(v);
29769         if(this.autoSize){
29770             var sz = this.boundEl.getSize();
29771             switch(this.autoSize){
29772                 case "width":
29773                 this.setSize(sz.width,  "");
29774                 break;
29775                 case "height":
29776                 this.setSize("",  sz.height);
29777                 break;
29778                 default:
29779                 this.setSize(sz.width,  sz.height);
29780             }
29781         }
29782         this.el.alignTo(this.boundEl, this.alignment);
29783         this.editing = true;
29784         if(Roo.QuickTips){
29785             Roo.QuickTips.disable();
29786         }
29787         this.show();
29788     },
29789
29790     /**
29791      * Sets the height and width of this editor.
29792      * @param {Number} width The new width
29793      * @param {Number} height The new height
29794      */
29795     setSize : function(w, h){
29796         this.field.setSize(w, h);
29797         if(this.el){
29798             this.el.sync();
29799         }
29800     },
29801
29802     /**
29803      * Realigns the editor to the bound field based on the current alignment config value.
29804      */
29805     realign : function(){
29806         this.el.alignTo(this.boundEl, this.alignment);
29807     },
29808
29809     /**
29810      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29811      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29812      */
29813     completeEdit : function(remainVisible){
29814         if(!this.editing){
29815             return;
29816         }
29817         var v = this.getValue();
29818         if(this.revertInvalid !== false && !this.field.isValid()){
29819             v = this.startValue;
29820             this.cancelEdit(true);
29821         }
29822         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29823             this.editing = false;
29824             this.hide();
29825             return;
29826         }
29827         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29828             this.editing = false;
29829             if(this.updateEl && this.boundEl){
29830                 this.boundEl.update(v);
29831             }
29832             if(remainVisible !== true){
29833                 this.hide();
29834             }
29835             this.fireEvent("complete", this, v, this.startValue);
29836         }
29837     },
29838
29839     // private
29840     onShow : function(){
29841         this.el.show();
29842         if(this.hideEl !== false){
29843             this.boundEl.hide();
29844         }
29845         this.field.show();
29846         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29847             this.fixIEFocus = true;
29848             this.deferredFocus.defer(50, this);
29849         }else{
29850             this.field.focus();
29851         }
29852         this.fireEvent("startedit", this.boundEl, this.startValue);
29853     },
29854
29855     deferredFocus : function(){
29856         if(this.editing){
29857             this.field.focus();
29858         }
29859     },
29860
29861     /**
29862      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29863      * reverted to the original starting value.
29864      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29865      * cancel (defaults to false)
29866      */
29867     cancelEdit : function(remainVisible){
29868         if(this.editing){
29869             this.setValue(this.startValue);
29870             if(remainVisible !== true){
29871                 this.hide();
29872             }
29873         }
29874     },
29875
29876     // private
29877     onBlur : function(){
29878         if(this.allowBlur !== true && this.editing){
29879             this.completeEdit();
29880         }
29881     },
29882
29883     // private
29884     onHide : function(){
29885         if(this.editing){
29886             this.completeEdit();
29887             return;
29888         }
29889         this.field.blur();
29890         if(this.field.collapse){
29891             this.field.collapse();
29892         }
29893         this.el.hide();
29894         if(this.hideEl !== false){
29895             this.boundEl.show();
29896         }
29897         if(Roo.QuickTips){
29898             Roo.QuickTips.enable();
29899         }
29900     },
29901
29902     /**
29903      * Sets the data value of the editor
29904      * @param {Mixed} value Any valid value supported by the underlying field
29905      */
29906     setValue : function(v){
29907         this.field.setValue(v);
29908     },
29909
29910     /**
29911      * Gets the data value of the editor
29912      * @return {Mixed} The data value
29913      */
29914     getValue : function(){
29915         return this.field.getValue();
29916     }
29917 });/*
29918  * Based on:
29919  * Ext JS Library 1.1.1
29920  * Copyright(c) 2006-2007, Ext JS, LLC.
29921  *
29922  * Originally Released Under LGPL - original licence link has changed is not relivant.
29923  *
29924  * Fork - LGPL
29925  * <script type="text/javascript">
29926  */
29927  
29928 /**
29929  * @class Roo.BasicDialog
29930  * @extends Roo.util.Observable
29931  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29932  * <pre><code>
29933 var dlg = new Roo.BasicDialog("my-dlg", {
29934     height: 200,
29935     width: 300,
29936     minHeight: 100,
29937     minWidth: 150,
29938     modal: true,
29939     proxyDrag: true,
29940     shadow: true
29941 });
29942 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29943 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29944 dlg.addButton('Cancel', dlg.hide, dlg);
29945 dlg.show();
29946 </code></pre>
29947   <b>A Dialog should always be a direct child of the body element.</b>
29948  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29949  * @cfg {String} title Default text to display in the title bar (defaults to null)
29950  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29951  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29952  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29953  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29954  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29955  * (defaults to null with no animation)
29956  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29957  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29958  * property for valid values (defaults to 'all')
29959  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29960  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29961  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29962  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29963  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29964  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29965  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29966  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29967  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29968  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29969  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29970  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29971  * draggable = true (defaults to false)
29972  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29973  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29974  * shadow (defaults to false)
29975  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29976  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29977  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29978  * @cfg {Array} buttons Array of buttons
29979  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29980  * @constructor
29981  * Create a new BasicDialog.
29982  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29983  * @param {Object} config Configuration options
29984  */
29985 Roo.BasicDialog = function(el, config){
29986     this.el = Roo.get(el);
29987     var dh = Roo.DomHelper;
29988     if(!this.el && config && config.autoCreate){
29989         if(typeof config.autoCreate == "object"){
29990             if(!config.autoCreate.id){
29991                 config.autoCreate.id = el;
29992             }
29993             this.el = dh.append(document.body,
29994                         config.autoCreate, true);
29995         }else{
29996             this.el = dh.append(document.body,
29997                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29998         }
29999     }
30000     el = this.el;
30001     el.setDisplayed(true);
30002     el.hide = this.hideAction;
30003     this.id = el.id;
30004     el.addClass("x-dlg");
30005
30006     Roo.apply(this, config);
30007
30008     this.proxy = el.createProxy("x-dlg-proxy");
30009     this.proxy.hide = this.hideAction;
30010     this.proxy.setOpacity(.5);
30011     this.proxy.hide();
30012
30013     if(config.width){
30014         el.setWidth(config.width);
30015     }
30016     if(config.height){
30017         el.setHeight(config.height);
30018     }
30019     this.size = el.getSize();
30020     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30021         this.xy = [config.x,config.y];
30022     }else{
30023         this.xy = el.getCenterXY(true);
30024     }
30025     /** The header element @type Roo.Element */
30026     this.header = el.child("> .x-dlg-hd");
30027     /** The body element @type Roo.Element */
30028     this.body = el.child("> .x-dlg-bd");
30029     /** The footer element @type Roo.Element */
30030     this.footer = el.child("> .x-dlg-ft");
30031
30032     if(!this.header){
30033         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30034     }
30035     if(!this.body){
30036         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30037     }
30038
30039     this.header.unselectable();
30040     if(this.title){
30041         this.header.update(this.title);
30042     }
30043     // this element allows the dialog to be focused for keyboard event
30044     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30045     this.focusEl.swallowEvent("click", true);
30046
30047     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30048
30049     // wrap the body and footer for special rendering
30050     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30051     if(this.footer){
30052         this.bwrap.dom.appendChild(this.footer.dom);
30053     }
30054
30055     this.bg = this.el.createChild({
30056         tag: "div", cls:"x-dlg-bg",
30057         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30058     });
30059     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30060
30061
30062     if(this.autoScroll !== false && !this.autoTabs){
30063         this.body.setStyle("overflow", "auto");
30064     }
30065
30066     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30067
30068     if(this.closable !== false){
30069         this.el.addClass("x-dlg-closable");
30070         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30071         this.close.on("click", this.closeClick, this);
30072         this.close.addClassOnOver("x-dlg-close-over");
30073     }
30074     if(this.collapsible !== false){
30075         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30076         this.collapseBtn.on("click", this.collapseClick, this);
30077         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30078         this.header.on("dblclick", this.collapseClick, this);
30079     }
30080     if(this.resizable !== false){
30081         this.el.addClass("x-dlg-resizable");
30082         this.resizer = new Roo.Resizable(el, {
30083             minWidth: this.minWidth || 80,
30084             minHeight:this.minHeight || 80,
30085             handles: this.resizeHandles || "all",
30086             pinned: true
30087         });
30088         this.resizer.on("beforeresize", this.beforeResize, this);
30089         this.resizer.on("resize", this.onResize, this);
30090     }
30091     if(this.draggable !== false){
30092         el.addClass("x-dlg-draggable");
30093         if (!this.proxyDrag) {
30094             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30095         }
30096         else {
30097             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30098         }
30099         dd.setHandleElId(this.header.id);
30100         dd.endDrag = this.endMove.createDelegate(this);
30101         dd.startDrag = this.startMove.createDelegate(this);
30102         dd.onDrag = this.onDrag.createDelegate(this);
30103         dd.scroll = false;
30104         this.dd = dd;
30105     }
30106     if(this.modal){
30107         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30108         this.mask.enableDisplayMode("block");
30109         this.mask.hide();
30110         this.el.addClass("x-dlg-modal");
30111     }
30112     if(this.shadow){
30113         this.shadow = new Roo.Shadow({
30114             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30115             offset : this.shadowOffset
30116         });
30117     }else{
30118         this.shadowOffset = 0;
30119     }
30120     if(Roo.useShims && this.shim !== false){
30121         this.shim = this.el.createShim();
30122         this.shim.hide = this.hideAction;
30123         this.shim.hide();
30124     }else{
30125         this.shim = false;
30126     }
30127     if(this.autoTabs){
30128         this.initTabs();
30129     }
30130     if (this.buttons) { 
30131         var bts= this.buttons;
30132         this.buttons = [];
30133         Roo.each(bts, function(b) {
30134             this.addButton(b);
30135         }, this);
30136     }
30137     
30138     
30139     this.addEvents({
30140         /**
30141          * @event keydown
30142          * Fires when a key is pressed
30143          * @param {Roo.BasicDialog} this
30144          * @param {Roo.EventObject} e
30145          */
30146         "keydown" : true,
30147         /**
30148          * @event move
30149          * Fires when this dialog is moved by the user.
30150          * @param {Roo.BasicDialog} this
30151          * @param {Number} x The new page X
30152          * @param {Number} y The new page Y
30153          */
30154         "move" : true,
30155         /**
30156          * @event resize
30157          * Fires when this dialog is resized by the user.
30158          * @param {Roo.BasicDialog} this
30159          * @param {Number} width The new width
30160          * @param {Number} height The new height
30161          */
30162         "resize" : true,
30163         /**
30164          * @event beforehide
30165          * Fires before this dialog is hidden.
30166          * @param {Roo.BasicDialog} this
30167          */
30168         "beforehide" : true,
30169         /**
30170          * @event hide
30171          * Fires when this dialog is hidden.
30172          * @param {Roo.BasicDialog} this
30173          */
30174         "hide" : true,
30175         /**
30176          * @event beforeshow
30177          * Fires before this dialog is shown.
30178          * @param {Roo.BasicDialog} this
30179          */
30180         "beforeshow" : true,
30181         /**
30182          * @event show
30183          * Fires when this dialog is shown.
30184          * @param {Roo.BasicDialog} this
30185          */
30186         "show" : true
30187     });
30188     el.on("keydown", this.onKeyDown, this);
30189     el.on("mousedown", this.toFront, this);
30190     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30191     this.el.hide();
30192     Roo.DialogManager.register(this);
30193     Roo.BasicDialog.superclass.constructor.call(this);
30194 };
30195
30196 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30197     shadowOffset: Roo.isIE ? 6 : 5,
30198     minHeight: 80,
30199     minWidth: 200,
30200     minButtonWidth: 75,
30201     defaultButton: null,
30202     buttonAlign: "right",
30203     tabTag: 'div',
30204     firstShow: true,
30205
30206     /**
30207      * Sets the dialog title text
30208      * @param {String} text The title text to display
30209      * @return {Roo.BasicDialog} this
30210      */
30211     setTitle : function(text){
30212         this.header.update(text);
30213         return this;
30214     },
30215
30216     // private
30217     closeClick : function(){
30218         this.hide();
30219     },
30220
30221     // private
30222     collapseClick : function(){
30223         this[this.collapsed ? "expand" : "collapse"]();
30224     },
30225
30226     /**
30227      * Collapses the dialog to its minimized state (only the title bar is visible).
30228      * Equivalent to the user clicking the collapse dialog button.
30229      */
30230     collapse : function(){
30231         if(!this.collapsed){
30232             this.collapsed = true;
30233             this.el.addClass("x-dlg-collapsed");
30234             this.restoreHeight = this.el.getHeight();
30235             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30236         }
30237     },
30238
30239     /**
30240      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30241      * clicking the expand dialog button.
30242      */
30243     expand : function(){
30244         if(this.collapsed){
30245             this.collapsed = false;
30246             this.el.removeClass("x-dlg-collapsed");
30247             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30248         }
30249     },
30250
30251     /**
30252      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30253      * @return {Roo.TabPanel} The tabs component
30254      */
30255     initTabs : function(){
30256         var tabs = this.getTabs();
30257         while(tabs.getTab(0)){
30258             tabs.removeTab(0);
30259         }
30260         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30261             var dom = el.dom;
30262             tabs.addTab(Roo.id(dom), dom.title);
30263             dom.title = "";
30264         });
30265         tabs.activate(0);
30266         return tabs;
30267     },
30268
30269     // private
30270     beforeResize : function(){
30271         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30272     },
30273
30274     // private
30275     onResize : function(){
30276         this.refreshSize();
30277         this.syncBodyHeight();
30278         this.adjustAssets();
30279         this.focus();
30280         this.fireEvent("resize", this, this.size.width, this.size.height);
30281     },
30282
30283     // private
30284     onKeyDown : function(e){
30285         if(this.isVisible()){
30286             this.fireEvent("keydown", this, e);
30287         }
30288     },
30289
30290     /**
30291      * Resizes the dialog.
30292      * @param {Number} width
30293      * @param {Number} height
30294      * @return {Roo.BasicDialog} this
30295      */
30296     resizeTo : function(width, height){
30297         this.el.setSize(width, height);
30298         this.size = {width: width, height: height};
30299         this.syncBodyHeight();
30300         if(this.fixedcenter){
30301             this.center();
30302         }
30303         if(this.isVisible()){
30304             this.constrainXY();
30305             this.adjustAssets();
30306         }
30307         this.fireEvent("resize", this, width, height);
30308         return this;
30309     },
30310
30311
30312     /**
30313      * Resizes the dialog to fit the specified content size.
30314      * @param {Number} width
30315      * @param {Number} height
30316      * @return {Roo.BasicDialog} this
30317      */
30318     setContentSize : function(w, h){
30319         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30320         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30321         //if(!this.el.isBorderBox()){
30322             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30323             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30324         //}
30325         if(this.tabs){
30326             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30327             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30328         }
30329         this.resizeTo(w, h);
30330         return this;
30331     },
30332
30333     /**
30334      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30335      * executed in response to a particular key being pressed while the dialog is active.
30336      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30337      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30338      * @param {Function} fn The function to call
30339      * @param {Object} scope (optional) The scope of the function
30340      * @return {Roo.BasicDialog} this
30341      */
30342     addKeyListener : function(key, fn, scope){
30343         var keyCode, shift, ctrl, alt;
30344         if(typeof key == "object" && !(key instanceof Array)){
30345             keyCode = key["key"];
30346             shift = key["shift"];
30347             ctrl = key["ctrl"];
30348             alt = key["alt"];
30349         }else{
30350             keyCode = key;
30351         }
30352         var handler = function(dlg, e){
30353             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30354                 var k = e.getKey();
30355                 if(keyCode instanceof Array){
30356                     for(var i = 0, len = keyCode.length; i < len; i++){
30357                         if(keyCode[i] == k){
30358                           fn.call(scope || window, dlg, k, e);
30359                           return;
30360                         }
30361                     }
30362                 }else{
30363                     if(k == keyCode){
30364                         fn.call(scope || window, dlg, k, e);
30365                     }
30366                 }
30367             }
30368         };
30369         this.on("keydown", handler);
30370         return this;
30371     },
30372
30373     /**
30374      * Returns the TabPanel component (creates it if it doesn't exist).
30375      * Note: If you wish to simply check for the existence of tabs without creating them,
30376      * check for a null 'tabs' property.
30377      * @return {Roo.TabPanel} The tabs component
30378      */
30379     getTabs : function(){
30380         if(!this.tabs){
30381             this.el.addClass("x-dlg-auto-tabs");
30382             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30383             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30384         }
30385         return this.tabs;
30386     },
30387
30388     /**
30389      * Adds a button to the footer section of the dialog.
30390      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30391      * object or a valid Roo.DomHelper element config
30392      * @param {Function} handler The function called when the button is clicked
30393      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30394      * @return {Roo.Button} The new button
30395      */
30396     addButton : function(config, handler, scope){
30397         var dh = Roo.DomHelper;
30398         if(!this.footer){
30399             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30400         }
30401         if(!this.btnContainer){
30402             var tb = this.footer.createChild({
30403
30404                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30405                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30406             }, null, true);
30407             this.btnContainer = tb.firstChild.firstChild.firstChild;
30408         }
30409         var bconfig = {
30410             handler: handler,
30411             scope: scope,
30412             minWidth: this.minButtonWidth,
30413             hideParent:true
30414         };
30415         if(typeof config == "string"){
30416             bconfig.text = config;
30417         }else{
30418             if(config.tag){
30419                 bconfig.dhconfig = config;
30420             }else{
30421                 Roo.apply(bconfig, config);
30422             }
30423         }
30424         var fc = false;
30425         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30426             bconfig.position = Math.max(0, bconfig.position);
30427             fc = this.btnContainer.childNodes[bconfig.position];
30428         }
30429          
30430         var btn = new Roo.Button(
30431             fc ? 
30432                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30433                 : this.btnContainer.appendChild(document.createElement("td")),
30434             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30435             bconfig
30436         );
30437         this.syncBodyHeight();
30438         if(!this.buttons){
30439             /**
30440              * Array of all the buttons that have been added to this dialog via addButton
30441              * @type Array
30442              */
30443             this.buttons = [];
30444         }
30445         this.buttons.push(btn);
30446         return btn;
30447     },
30448
30449     /**
30450      * Sets the default button to be focused when the dialog is displayed.
30451      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30452      * @return {Roo.BasicDialog} this
30453      */
30454     setDefaultButton : function(btn){
30455         this.defaultButton = btn;
30456         return this;
30457     },
30458
30459     // private
30460     getHeaderFooterHeight : function(safe){
30461         var height = 0;
30462         if(this.header){
30463            height += this.header.getHeight();
30464         }
30465         if(this.footer){
30466            var fm = this.footer.getMargins();
30467             height += (this.footer.getHeight()+fm.top+fm.bottom);
30468         }
30469         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30470         height += this.centerBg.getPadding("tb");
30471         return height;
30472     },
30473
30474     // private
30475     syncBodyHeight : function()
30476     {
30477         var bd = this.body, // the text
30478             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30479             bw = this.bwrap;
30480         var height = this.size.height - this.getHeaderFooterHeight(false);
30481         bd.setHeight(height-bd.getMargins("tb"));
30482         var hh = this.header.getHeight();
30483         var h = this.size.height-hh;
30484         cb.setHeight(h);
30485         
30486         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30487         bw.setHeight(h-cb.getPadding("tb"));
30488         
30489         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30490         bd.setWidth(bw.getWidth(true));
30491         if(this.tabs){
30492             this.tabs.syncHeight();
30493             if(Roo.isIE){
30494                 this.tabs.el.repaint();
30495             }
30496         }
30497     },
30498
30499     /**
30500      * Restores the previous state of the dialog if Roo.state is configured.
30501      * @return {Roo.BasicDialog} this
30502      */
30503     restoreState : function(){
30504         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30505         if(box && box.width){
30506             this.xy = [box.x, box.y];
30507             this.resizeTo(box.width, box.height);
30508         }
30509         return this;
30510     },
30511
30512     // private
30513     beforeShow : function(){
30514         this.expand();
30515         if(this.fixedcenter){
30516             this.xy = this.el.getCenterXY(true);
30517         }
30518         if(this.modal){
30519             Roo.get(document.body).addClass("x-body-masked");
30520             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30521             this.mask.show();
30522         }
30523         this.constrainXY();
30524     },
30525
30526     // private
30527     animShow : function(){
30528         var b = Roo.get(this.animateTarget).getBox();
30529         this.proxy.setSize(b.width, b.height);
30530         this.proxy.setLocation(b.x, b.y);
30531         this.proxy.show();
30532         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30533                     true, .35, this.showEl.createDelegate(this));
30534     },
30535
30536     /**
30537      * Shows the dialog.
30538      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30539      * @return {Roo.BasicDialog} this
30540      */
30541     show : function(animateTarget){
30542         if (this.fireEvent("beforeshow", this) === false){
30543             return;
30544         }
30545         if(this.syncHeightBeforeShow){
30546             this.syncBodyHeight();
30547         }else if(this.firstShow){
30548             this.firstShow = false;
30549             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30550         }
30551         this.animateTarget = animateTarget || this.animateTarget;
30552         if(!this.el.isVisible()){
30553             this.beforeShow();
30554             if(this.animateTarget && Roo.get(this.animateTarget)){
30555                 this.animShow();
30556             }else{
30557                 this.showEl();
30558             }
30559         }
30560         return this;
30561     },
30562
30563     // private
30564     showEl : function(){
30565         this.proxy.hide();
30566         this.el.setXY(this.xy);
30567         this.el.show();
30568         this.adjustAssets(true);
30569         this.toFront();
30570         this.focus();
30571         // IE peekaboo bug - fix found by Dave Fenwick
30572         if(Roo.isIE){
30573             this.el.repaint();
30574         }
30575         this.fireEvent("show", this);
30576     },
30577
30578     /**
30579      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30580      * dialog itself will receive focus.
30581      */
30582     focus : function(){
30583         if(this.defaultButton){
30584             this.defaultButton.focus();
30585         }else{
30586             this.focusEl.focus();
30587         }
30588     },
30589
30590     // private
30591     constrainXY : function(){
30592         if(this.constraintoviewport !== false){
30593             if(!this.viewSize){
30594                 if(this.container){
30595                     var s = this.container.getSize();
30596                     this.viewSize = [s.width, s.height];
30597                 }else{
30598                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30599                 }
30600             }
30601             var s = Roo.get(this.container||document).getScroll();
30602
30603             var x = this.xy[0], y = this.xy[1];
30604             var w = this.size.width, h = this.size.height;
30605             var vw = this.viewSize[0], vh = this.viewSize[1];
30606             // only move it if it needs it
30607             var moved = false;
30608             // first validate right/bottom
30609             if(x + w > vw+s.left){
30610                 x = vw - w;
30611                 moved = true;
30612             }
30613             if(y + h > vh+s.top){
30614                 y = vh - h;
30615                 moved = true;
30616             }
30617             // then make sure top/left isn't negative
30618             if(x < s.left){
30619                 x = s.left;
30620                 moved = true;
30621             }
30622             if(y < s.top){
30623                 y = s.top;
30624                 moved = true;
30625             }
30626             if(moved){
30627                 // cache xy
30628                 this.xy = [x, y];
30629                 if(this.isVisible()){
30630                     this.el.setLocation(x, y);
30631                     this.adjustAssets();
30632                 }
30633             }
30634         }
30635     },
30636
30637     // private
30638     onDrag : function(){
30639         if(!this.proxyDrag){
30640             this.xy = this.el.getXY();
30641             this.adjustAssets();
30642         }
30643     },
30644
30645     // private
30646     adjustAssets : function(doShow){
30647         var x = this.xy[0], y = this.xy[1];
30648         var w = this.size.width, h = this.size.height;
30649         if(doShow === true){
30650             if(this.shadow){
30651                 this.shadow.show(this.el);
30652             }
30653             if(this.shim){
30654                 this.shim.show();
30655             }
30656         }
30657         if(this.shadow && this.shadow.isVisible()){
30658             this.shadow.show(this.el);
30659         }
30660         if(this.shim && this.shim.isVisible()){
30661             this.shim.setBounds(x, y, w, h);
30662         }
30663     },
30664
30665     // private
30666     adjustViewport : function(w, h){
30667         if(!w || !h){
30668             w = Roo.lib.Dom.getViewWidth();
30669             h = Roo.lib.Dom.getViewHeight();
30670         }
30671         // cache the size
30672         this.viewSize = [w, h];
30673         if(this.modal && this.mask.isVisible()){
30674             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30675             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30676         }
30677         if(this.isVisible()){
30678             this.constrainXY();
30679         }
30680     },
30681
30682     /**
30683      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30684      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30685      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30686      */
30687     destroy : function(removeEl){
30688         if(this.isVisible()){
30689             this.animateTarget = null;
30690             this.hide();
30691         }
30692         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30693         if(this.tabs){
30694             this.tabs.destroy(removeEl);
30695         }
30696         Roo.destroy(
30697              this.shim,
30698              this.proxy,
30699              this.resizer,
30700              this.close,
30701              this.mask
30702         );
30703         if(this.dd){
30704             this.dd.unreg();
30705         }
30706         if(this.buttons){
30707            for(var i = 0, len = this.buttons.length; i < len; i++){
30708                this.buttons[i].destroy();
30709            }
30710         }
30711         this.el.removeAllListeners();
30712         if(removeEl === true){
30713             this.el.update("");
30714             this.el.remove();
30715         }
30716         Roo.DialogManager.unregister(this);
30717     },
30718
30719     // private
30720     startMove : function(){
30721         if(this.proxyDrag){
30722             this.proxy.show();
30723         }
30724         if(this.constraintoviewport !== false){
30725             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30726         }
30727     },
30728
30729     // private
30730     endMove : function(){
30731         if(!this.proxyDrag){
30732             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30733         }else{
30734             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30735             this.proxy.hide();
30736         }
30737         this.refreshSize();
30738         this.adjustAssets();
30739         this.focus();
30740         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30741     },
30742
30743     /**
30744      * Brings this dialog to the front of any other visible dialogs
30745      * @return {Roo.BasicDialog} this
30746      */
30747     toFront : function(){
30748         Roo.DialogManager.bringToFront(this);
30749         return this;
30750     },
30751
30752     /**
30753      * Sends this dialog to the back (under) of any other visible dialogs
30754      * @return {Roo.BasicDialog} this
30755      */
30756     toBack : function(){
30757         Roo.DialogManager.sendToBack(this);
30758         return this;
30759     },
30760
30761     /**
30762      * Centers this dialog in the viewport
30763      * @return {Roo.BasicDialog} this
30764      */
30765     center : function(){
30766         var xy = this.el.getCenterXY(true);
30767         this.moveTo(xy[0], xy[1]);
30768         return this;
30769     },
30770
30771     /**
30772      * Moves the dialog's top-left corner to the specified point
30773      * @param {Number} x
30774      * @param {Number} y
30775      * @return {Roo.BasicDialog} this
30776      */
30777     moveTo : function(x, y){
30778         this.xy = [x,y];
30779         if(this.isVisible()){
30780             this.el.setXY(this.xy);
30781             this.adjustAssets();
30782         }
30783         return this;
30784     },
30785
30786     /**
30787      * Aligns the dialog to the specified element
30788      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30789      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30790      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30791      * @return {Roo.BasicDialog} this
30792      */
30793     alignTo : function(element, position, offsets){
30794         this.xy = this.el.getAlignToXY(element, position, offsets);
30795         if(this.isVisible()){
30796             this.el.setXY(this.xy);
30797             this.adjustAssets();
30798         }
30799         return this;
30800     },
30801
30802     /**
30803      * Anchors an element to another element and realigns it when the window is resized.
30804      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30805      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30806      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30807      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30808      * is a number, it is used as the buffer delay (defaults to 50ms).
30809      * @return {Roo.BasicDialog} this
30810      */
30811     anchorTo : function(el, alignment, offsets, monitorScroll){
30812         var action = function(){
30813             this.alignTo(el, alignment, offsets);
30814         };
30815         Roo.EventManager.onWindowResize(action, this);
30816         var tm = typeof monitorScroll;
30817         if(tm != 'undefined'){
30818             Roo.EventManager.on(window, 'scroll', action, this,
30819                 {buffer: tm == 'number' ? monitorScroll : 50});
30820         }
30821         action.call(this);
30822         return this;
30823     },
30824
30825     /**
30826      * Returns true if the dialog is visible
30827      * @return {Boolean}
30828      */
30829     isVisible : function(){
30830         return this.el.isVisible();
30831     },
30832
30833     // private
30834     animHide : function(callback){
30835         var b = Roo.get(this.animateTarget).getBox();
30836         this.proxy.show();
30837         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30838         this.el.hide();
30839         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30840                     this.hideEl.createDelegate(this, [callback]));
30841     },
30842
30843     /**
30844      * Hides the dialog.
30845      * @param {Function} callback (optional) Function to call when the dialog is hidden
30846      * @return {Roo.BasicDialog} this
30847      */
30848     hide : function(callback){
30849         if (this.fireEvent("beforehide", this) === false){
30850             return;
30851         }
30852         if(this.shadow){
30853             this.shadow.hide();
30854         }
30855         if(this.shim) {
30856           this.shim.hide();
30857         }
30858         // sometimes animateTarget seems to get set.. causing problems...
30859         // this just double checks..
30860         if(this.animateTarget && Roo.get(this.animateTarget)) {
30861            this.animHide(callback);
30862         }else{
30863             this.el.hide();
30864             this.hideEl(callback);
30865         }
30866         return this;
30867     },
30868
30869     // private
30870     hideEl : function(callback){
30871         this.proxy.hide();
30872         if(this.modal){
30873             this.mask.hide();
30874             Roo.get(document.body).removeClass("x-body-masked");
30875         }
30876         this.fireEvent("hide", this);
30877         if(typeof callback == "function"){
30878             callback();
30879         }
30880     },
30881
30882     // private
30883     hideAction : function(){
30884         this.setLeft("-10000px");
30885         this.setTop("-10000px");
30886         this.setStyle("visibility", "hidden");
30887     },
30888
30889     // private
30890     refreshSize : function(){
30891         this.size = this.el.getSize();
30892         this.xy = this.el.getXY();
30893         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30894     },
30895
30896     // private
30897     // z-index is managed by the DialogManager and may be overwritten at any time
30898     setZIndex : function(index){
30899         if(this.modal){
30900             this.mask.setStyle("z-index", index);
30901         }
30902         if(this.shim){
30903             this.shim.setStyle("z-index", ++index);
30904         }
30905         if(this.shadow){
30906             this.shadow.setZIndex(++index);
30907         }
30908         this.el.setStyle("z-index", ++index);
30909         if(this.proxy){
30910             this.proxy.setStyle("z-index", ++index);
30911         }
30912         if(this.resizer){
30913             this.resizer.proxy.setStyle("z-index", ++index);
30914         }
30915
30916         this.lastZIndex = index;
30917     },
30918
30919     /**
30920      * Returns the element for this dialog
30921      * @return {Roo.Element} The underlying dialog Element
30922      */
30923     getEl : function(){
30924         return this.el;
30925     }
30926 });
30927
30928 /**
30929  * @class Roo.DialogManager
30930  * Provides global access to BasicDialogs that have been created and
30931  * support for z-indexing (layering) multiple open dialogs.
30932  */
30933 Roo.DialogManager = function(){
30934     var list = {};
30935     var accessList = [];
30936     var front = null;
30937
30938     // private
30939     var sortDialogs = function(d1, d2){
30940         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30941     };
30942
30943     // private
30944     var orderDialogs = function(){
30945         accessList.sort(sortDialogs);
30946         var seed = Roo.DialogManager.zseed;
30947         for(var i = 0, len = accessList.length; i < len; i++){
30948             var dlg = accessList[i];
30949             if(dlg){
30950                 dlg.setZIndex(seed + (i*10));
30951             }
30952         }
30953     };
30954
30955     return {
30956         /**
30957          * The starting z-index for BasicDialogs (defaults to 9000)
30958          * @type Number The z-index value
30959          */
30960         zseed : 9000,
30961
30962         // private
30963         register : function(dlg){
30964             list[dlg.id] = dlg;
30965             accessList.push(dlg);
30966         },
30967
30968         // private
30969         unregister : function(dlg){
30970             delete list[dlg.id];
30971             var i=0;
30972             var len=0;
30973             if(!accessList.indexOf){
30974                 for(  i = 0, len = accessList.length; i < len; i++){
30975                     if(accessList[i] == dlg){
30976                         accessList.splice(i, 1);
30977                         return;
30978                     }
30979                 }
30980             }else{
30981                  i = accessList.indexOf(dlg);
30982                 if(i != -1){
30983                     accessList.splice(i, 1);
30984                 }
30985             }
30986         },
30987
30988         /**
30989          * Gets a registered dialog by id
30990          * @param {String/Object} id The id of the dialog or a dialog
30991          * @return {Roo.BasicDialog} this
30992          */
30993         get : function(id){
30994             return typeof id == "object" ? id : list[id];
30995         },
30996
30997         /**
30998          * Brings the specified dialog to the front
30999          * @param {String/Object} dlg The id of the dialog or a dialog
31000          * @return {Roo.BasicDialog} this
31001          */
31002         bringToFront : function(dlg){
31003             dlg = this.get(dlg);
31004             if(dlg != front){
31005                 front = dlg;
31006                 dlg._lastAccess = new Date().getTime();
31007                 orderDialogs();
31008             }
31009             return dlg;
31010         },
31011
31012         /**
31013          * Sends the specified dialog to the back
31014          * @param {String/Object} dlg The id of the dialog or a dialog
31015          * @return {Roo.BasicDialog} this
31016          */
31017         sendToBack : function(dlg){
31018             dlg = this.get(dlg);
31019             dlg._lastAccess = -(new Date().getTime());
31020             orderDialogs();
31021             return dlg;
31022         },
31023
31024         /**
31025          * Hides all dialogs
31026          */
31027         hideAll : function(){
31028             for(var id in list){
31029                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31030                     list[id].hide();
31031                 }
31032             }
31033         }
31034     };
31035 }();
31036
31037 /**
31038  * @class Roo.LayoutDialog
31039  * @extends Roo.BasicDialog
31040  * Dialog which provides adjustments for working with a layout in a Dialog.
31041  * Add your necessary layout config options to the dialog's config.<br>
31042  * Example usage (including a nested layout):
31043  * <pre><code>
31044 if(!dialog){
31045     dialog = new Roo.LayoutDialog("download-dlg", {
31046         modal: true,
31047         width:600,
31048         height:450,
31049         shadow:true,
31050         minWidth:500,
31051         minHeight:350,
31052         autoTabs:true,
31053         proxyDrag:true,
31054         // layout config merges with the dialog config
31055         center:{
31056             tabPosition: "top",
31057             alwaysShowTabs: true
31058         }
31059     });
31060     dialog.addKeyListener(27, dialog.hide, dialog);
31061     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31062     dialog.addButton("Build It!", this.getDownload, this);
31063
31064     // we can even add nested layouts
31065     var innerLayout = new Roo.BorderLayout("dl-inner", {
31066         east: {
31067             initialSize: 200,
31068             autoScroll:true,
31069             split:true
31070         },
31071         center: {
31072             autoScroll:true
31073         }
31074     });
31075     innerLayout.beginUpdate();
31076     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31077     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31078     innerLayout.endUpdate(true);
31079
31080     var layout = dialog.getLayout();
31081     layout.beginUpdate();
31082     layout.add("center", new Roo.ContentPanel("standard-panel",
31083                         {title: "Download the Source", fitToFrame:true}));
31084     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31085                {title: "Build your own roo.js"}));
31086     layout.getRegion("center").showPanel(sp);
31087     layout.endUpdate();
31088 }
31089 </code></pre>
31090     * @constructor
31091     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31092     * @param {Object} config configuration options
31093   */
31094 Roo.LayoutDialog = function(el, cfg){
31095     
31096     var config=  cfg;
31097     if (typeof(cfg) == 'undefined') {
31098         config = Roo.apply({}, el);
31099         // not sure why we use documentElement here.. - it should always be body.
31100         // IE7 borks horribly if we use documentElement.
31101         // webkit also does not like documentElement - it creates a body element...
31102         el = Roo.get( document.body || document.documentElement ).createChild();
31103         //config.autoCreate = true;
31104     }
31105     
31106     
31107     config.autoTabs = false;
31108     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31109     this.body.setStyle({overflow:"hidden", position:"relative"});
31110     this.layout = new Roo.BorderLayout(this.body.dom, config);
31111     this.layout.monitorWindowResize = false;
31112     this.el.addClass("x-dlg-auto-layout");
31113     // fix case when center region overwrites center function
31114     this.center = Roo.BasicDialog.prototype.center;
31115     this.on("show", this.layout.layout, this.layout, true);
31116     if (config.items) {
31117         var xitems = config.items;
31118         delete config.items;
31119         Roo.each(xitems, this.addxtype, this);
31120     }
31121     
31122     
31123 };
31124 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31125     /**
31126      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31127      * @deprecated
31128      */
31129     endUpdate : function(){
31130         this.layout.endUpdate();
31131     },
31132
31133     /**
31134      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31135      *  @deprecated
31136      */
31137     beginUpdate : function(){
31138         this.layout.beginUpdate();
31139     },
31140
31141     /**
31142      * Get the BorderLayout for this dialog
31143      * @return {Roo.BorderLayout}
31144      */
31145     getLayout : function(){
31146         return this.layout;
31147     },
31148
31149     showEl : function(){
31150         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31151         if(Roo.isIE7){
31152             this.layout.layout();
31153         }
31154     },
31155
31156     // private
31157     // Use the syncHeightBeforeShow config option to control this automatically
31158     syncBodyHeight : function(){
31159         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31160         if(this.layout){this.layout.layout();}
31161     },
31162     
31163       /**
31164      * Add an xtype element (actually adds to the layout.)
31165      * @return {Object} xdata xtype object data.
31166      */
31167     
31168     addxtype : function(c) {
31169         return this.layout.addxtype(c);
31170     }
31171 });/*
31172  * Based on:
31173  * Ext JS Library 1.1.1
31174  * Copyright(c) 2006-2007, Ext JS, LLC.
31175  *
31176  * Originally Released Under LGPL - original licence link has changed is not relivant.
31177  *
31178  * Fork - LGPL
31179  * <script type="text/javascript">
31180  */
31181  
31182 /**
31183  * @class Roo.MessageBox
31184  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31185  * Example usage:
31186  *<pre><code>
31187 // Basic alert:
31188 Roo.Msg.alert('Status', 'Changes saved successfully.');
31189
31190 // Prompt for user data:
31191 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31192     if (btn == 'ok'){
31193         // process text value...
31194     }
31195 });
31196
31197 // Show a dialog using config options:
31198 Roo.Msg.show({
31199    title:'Save Changes?',
31200    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31201    buttons: Roo.Msg.YESNOCANCEL,
31202    fn: processResult,
31203    animEl: 'elId'
31204 });
31205 </code></pre>
31206  * @singleton
31207  */
31208 Roo.MessageBox = function(){
31209     var dlg, opt, mask, waitTimer;
31210     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31211     var buttons, activeTextEl, bwidth;
31212
31213     // private
31214     var handleButton = function(button){
31215         dlg.hide();
31216         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31217     };
31218
31219     // private
31220     var handleHide = function(){
31221         if(opt && opt.cls){
31222             dlg.el.removeClass(opt.cls);
31223         }
31224         if(waitTimer){
31225             Roo.TaskMgr.stop(waitTimer);
31226             waitTimer = null;
31227         }
31228     };
31229
31230     // private
31231     var updateButtons = function(b){
31232         var width = 0;
31233         if(!b){
31234             buttons["ok"].hide();
31235             buttons["cancel"].hide();
31236             buttons["yes"].hide();
31237             buttons["no"].hide();
31238             dlg.footer.dom.style.display = 'none';
31239             return width;
31240         }
31241         dlg.footer.dom.style.display = '';
31242         for(var k in buttons){
31243             if(typeof buttons[k] != "function"){
31244                 if(b[k]){
31245                     buttons[k].show();
31246                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31247                     width += buttons[k].el.getWidth()+15;
31248                 }else{
31249                     buttons[k].hide();
31250                 }
31251             }
31252         }
31253         return width;
31254     };
31255
31256     // private
31257     var handleEsc = function(d, k, e){
31258         if(opt && opt.closable !== false){
31259             dlg.hide();
31260         }
31261         if(e){
31262             e.stopEvent();
31263         }
31264     };
31265
31266     return {
31267         /**
31268          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31269          * @return {Roo.BasicDialog} The BasicDialog element
31270          */
31271         getDialog : function(){
31272            if(!dlg){
31273                 dlg = new Roo.BasicDialog("x-msg-box", {
31274                     autoCreate : true,
31275                     shadow: true,
31276                     draggable: true,
31277                     resizable:false,
31278                     constraintoviewport:false,
31279                     fixedcenter:true,
31280                     collapsible : false,
31281                     shim:true,
31282                     modal: true,
31283                     width:400, height:100,
31284                     buttonAlign:"center",
31285                     closeClick : function(){
31286                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31287                             handleButton("no");
31288                         }else{
31289                             handleButton("cancel");
31290                         }
31291                     }
31292                 });
31293                 dlg.on("hide", handleHide);
31294                 mask = dlg.mask;
31295                 dlg.addKeyListener(27, handleEsc);
31296                 buttons = {};
31297                 var bt = this.buttonText;
31298                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31299                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31300                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31301                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31302                 bodyEl = dlg.body.createChild({
31303
31304                     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>'
31305                 });
31306                 msgEl = bodyEl.dom.firstChild;
31307                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31308                 textboxEl.enableDisplayMode();
31309                 textboxEl.addKeyListener([10,13], function(){
31310                     if(dlg.isVisible() && opt && opt.buttons){
31311                         if(opt.buttons.ok){
31312                             handleButton("ok");
31313                         }else if(opt.buttons.yes){
31314                             handleButton("yes");
31315                         }
31316                     }
31317                 });
31318                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31319                 textareaEl.enableDisplayMode();
31320                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31321                 progressEl.enableDisplayMode();
31322                 var pf = progressEl.dom.firstChild;
31323                 if (pf) {
31324                     pp = Roo.get(pf.firstChild);
31325                     pp.setHeight(pf.offsetHeight);
31326                 }
31327                 
31328             }
31329             return dlg;
31330         },
31331
31332         /**
31333          * Updates the message box body text
31334          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31335          * the XHTML-compliant non-breaking space character '&amp;#160;')
31336          * @return {Roo.MessageBox} This message box
31337          */
31338         updateText : function(text){
31339             if(!dlg.isVisible() && !opt.width){
31340                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31341             }
31342             msgEl.innerHTML = text || '&#160;';
31343       
31344             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31345             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31346             var w = Math.max(
31347                     Math.min(opt.width || cw , this.maxWidth), 
31348                     Math.max(opt.minWidth || this.minWidth, bwidth)
31349             );
31350             if(opt.prompt){
31351                 activeTextEl.setWidth(w);
31352             }
31353             if(dlg.isVisible()){
31354                 dlg.fixedcenter = false;
31355             }
31356             // to big, make it scroll. = But as usual stupid IE does not support
31357             // !important..
31358             
31359             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31360                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31361                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31362             } else {
31363                 bodyEl.dom.style.height = '';
31364                 bodyEl.dom.style.overflowY = '';
31365             }
31366             if (cw > w) {
31367                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31368             } else {
31369                 bodyEl.dom.style.overflowX = '';
31370             }
31371             
31372             dlg.setContentSize(w, bodyEl.getHeight());
31373             if(dlg.isVisible()){
31374                 dlg.fixedcenter = true;
31375             }
31376             return this;
31377         },
31378
31379         /**
31380          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31381          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31382          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31383          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31384          * @return {Roo.MessageBox} This message box
31385          */
31386         updateProgress : function(value, text){
31387             if(text){
31388                 this.updateText(text);
31389             }
31390             if (pp) { // weird bug on my firefox - for some reason this is not defined
31391                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31392             }
31393             return this;
31394         },        
31395
31396         /**
31397          * Returns true if the message box is currently displayed
31398          * @return {Boolean} True if the message box is visible, else false
31399          */
31400         isVisible : function(){
31401             return dlg && dlg.isVisible();  
31402         },
31403
31404         /**
31405          * Hides the message box if it is displayed
31406          */
31407         hide : function(){
31408             if(this.isVisible()){
31409                 dlg.hide();
31410             }  
31411         },
31412
31413         /**
31414          * Displays a new message box, or reinitializes an existing message box, based on the config options
31415          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31416          * The following config object properties are supported:
31417          * <pre>
31418 Property    Type             Description
31419 ----------  ---------------  ------------------------------------------------------------------------------------
31420 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31421                                    closes (defaults to undefined)
31422 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31423                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31424 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31425                                    progress and wait dialogs will ignore this property and always hide the
31426                                    close button as they can only be closed programmatically.
31427 cls               String           A custom CSS class to apply to the message box element
31428 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31429                                    displayed (defaults to 75)
31430 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31431                                    function will be btn (the name of the button that was clicked, if applicable,
31432                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31433                                    Progress and wait dialogs will ignore this option since they do not respond to
31434                                    user actions and can only be closed programmatically, so any required function
31435                                    should be called by the same code after it closes the dialog.
31436 icon              String           A CSS class that provides a background image to be used as an icon for
31437                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31438 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31439 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31440 modal             Boolean          False to allow user interaction with the page while the message box is
31441                                    displayed (defaults to true)
31442 msg               String           A string that will replace the existing message box body text (defaults
31443                                    to the XHTML-compliant non-breaking space character '&#160;')
31444 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31445 progress          Boolean          True to display a progress bar (defaults to false)
31446 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31447 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31448 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31449 title             String           The title text
31450 value             String           The string value to set into the active textbox element if displayed
31451 wait              Boolean          True to display a progress bar (defaults to false)
31452 width             Number           The width of the dialog in pixels
31453 </pre>
31454          *
31455          * Example usage:
31456          * <pre><code>
31457 Roo.Msg.show({
31458    title: 'Address',
31459    msg: 'Please enter your address:',
31460    width: 300,
31461    buttons: Roo.MessageBox.OKCANCEL,
31462    multiline: true,
31463    fn: saveAddress,
31464    animEl: 'addAddressBtn'
31465 });
31466 </code></pre>
31467          * @param {Object} config Configuration options
31468          * @return {Roo.MessageBox} This message box
31469          */
31470         show : function(options)
31471         {
31472             
31473             // this causes nightmares if you show one dialog after another
31474             // especially on callbacks..
31475              
31476             if(this.isVisible()){
31477                 
31478                 this.hide();
31479                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31480                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31481                 Roo.log("New Dialog Message:" +  options.msg )
31482                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31483                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31484                 
31485             }
31486             var d = this.getDialog();
31487             opt = options;
31488             d.setTitle(opt.title || "&#160;");
31489             d.close.setDisplayed(opt.closable !== false);
31490             activeTextEl = textboxEl;
31491             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31492             if(opt.prompt){
31493                 if(opt.multiline){
31494                     textboxEl.hide();
31495                     textareaEl.show();
31496                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31497                         opt.multiline : this.defaultTextHeight);
31498                     activeTextEl = textareaEl;
31499                 }else{
31500                     textboxEl.show();
31501                     textareaEl.hide();
31502                 }
31503             }else{
31504                 textboxEl.hide();
31505                 textareaEl.hide();
31506             }
31507             progressEl.setDisplayed(opt.progress === true);
31508             this.updateProgress(0);
31509             activeTextEl.dom.value = opt.value || "";
31510             if(opt.prompt){
31511                 dlg.setDefaultButton(activeTextEl);
31512             }else{
31513                 var bs = opt.buttons;
31514                 var db = null;
31515                 if(bs && bs.ok){
31516                     db = buttons["ok"];
31517                 }else if(bs && bs.yes){
31518                     db = buttons["yes"];
31519                 }
31520                 dlg.setDefaultButton(db);
31521             }
31522             bwidth = updateButtons(opt.buttons);
31523             this.updateText(opt.msg);
31524             if(opt.cls){
31525                 d.el.addClass(opt.cls);
31526             }
31527             d.proxyDrag = opt.proxyDrag === true;
31528             d.modal = opt.modal !== false;
31529             d.mask = opt.modal !== false ? mask : false;
31530             if(!d.isVisible()){
31531                 // force it to the end of the z-index stack so it gets a cursor in FF
31532                 document.body.appendChild(dlg.el.dom);
31533                 d.animateTarget = null;
31534                 d.show(options.animEl);
31535             }
31536             return this;
31537         },
31538
31539         /**
31540          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31541          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31542          * and closing the message box when the process is complete.
31543          * @param {String} title The title bar text
31544          * @param {String} msg The message box body text
31545          * @return {Roo.MessageBox} This message box
31546          */
31547         progress : function(title, msg){
31548             this.show({
31549                 title : title,
31550                 msg : msg,
31551                 buttons: false,
31552                 progress:true,
31553                 closable:false,
31554                 minWidth: this.minProgressWidth,
31555                 modal : true
31556             });
31557             return this;
31558         },
31559
31560         /**
31561          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31562          * If a callback function is passed it will be called after the user clicks the button, and the
31563          * id of the button that was clicked will be passed as the only parameter to the callback
31564          * (could also be the top-right close button).
31565          * @param {String} title The title bar text
31566          * @param {String} msg The message box body text
31567          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31568          * @param {Object} scope (optional) The scope of the callback function
31569          * @return {Roo.MessageBox} This message box
31570          */
31571         alert : function(title, msg, fn, scope){
31572             this.show({
31573                 title : title,
31574                 msg : msg,
31575                 buttons: this.OK,
31576                 fn: fn,
31577                 scope : scope,
31578                 modal : true
31579             });
31580             return this;
31581         },
31582
31583         /**
31584          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31585          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31586          * You are responsible for closing the message box when the process is complete.
31587          * @param {String} msg The message box body text
31588          * @param {String} title (optional) The title bar text
31589          * @return {Roo.MessageBox} This message box
31590          */
31591         wait : function(msg, title){
31592             this.show({
31593                 title : title,
31594                 msg : msg,
31595                 buttons: false,
31596                 closable:false,
31597                 progress:true,
31598                 modal:true,
31599                 width:300,
31600                 wait:true
31601             });
31602             waitTimer = Roo.TaskMgr.start({
31603                 run: function(i){
31604                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31605                 },
31606                 interval: 1000
31607             });
31608             return this;
31609         },
31610
31611         /**
31612          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31613          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31614          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31615          * @param {String} title The title bar text
31616          * @param {String} msg The message box body text
31617          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31618          * @param {Object} scope (optional) The scope of the callback function
31619          * @return {Roo.MessageBox} This message box
31620          */
31621         confirm : function(title, msg, fn, scope){
31622             this.show({
31623                 title : title,
31624                 msg : msg,
31625                 buttons: this.YESNO,
31626                 fn: fn,
31627                 scope : scope,
31628                 modal : true
31629             });
31630             return this;
31631         },
31632
31633         /**
31634          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31635          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31636          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31637          * (could also be the top-right close button) and the text that was entered will be passed as the two
31638          * parameters to the callback.
31639          * @param {String} title The title bar text
31640          * @param {String} msg The message box body text
31641          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31642          * @param {Object} scope (optional) The scope of the callback function
31643          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31644          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31645          * @return {Roo.MessageBox} This message box
31646          */
31647         prompt : function(title, msg, fn, scope, multiline){
31648             this.show({
31649                 title : title,
31650                 msg : msg,
31651                 buttons: this.OKCANCEL,
31652                 fn: fn,
31653                 minWidth:250,
31654                 scope : scope,
31655                 prompt:true,
31656                 multiline: multiline,
31657                 modal : true
31658             });
31659             return this;
31660         },
31661
31662         /**
31663          * Button config that displays a single OK button
31664          * @type Object
31665          */
31666         OK : {ok:true},
31667         /**
31668          * Button config that displays Yes and No buttons
31669          * @type Object
31670          */
31671         YESNO : {yes:true, no:true},
31672         /**
31673          * Button config that displays OK and Cancel buttons
31674          * @type Object
31675          */
31676         OKCANCEL : {ok:true, cancel:true},
31677         /**
31678          * Button config that displays Yes, No and Cancel buttons
31679          * @type Object
31680          */
31681         YESNOCANCEL : {yes:true, no:true, cancel:true},
31682
31683         /**
31684          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31685          * @type Number
31686          */
31687         defaultTextHeight : 75,
31688         /**
31689          * The maximum width in pixels of the message box (defaults to 600)
31690          * @type Number
31691          */
31692         maxWidth : 600,
31693         /**
31694          * The minimum width in pixels of the message box (defaults to 100)
31695          * @type Number
31696          */
31697         minWidth : 100,
31698         /**
31699          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31700          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31701          * @type Number
31702          */
31703         minProgressWidth : 250,
31704         /**
31705          * An object containing the default button text strings that can be overriden for localized language support.
31706          * Supported properties are: ok, cancel, yes and no.
31707          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31708          * @type Object
31709          */
31710         buttonText : {
31711             ok : "OK",
31712             cancel : "Cancel",
31713             yes : "Yes",
31714             no : "No"
31715         }
31716     };
31717 }();
31718
31719 /**
31720  * Shorthand for {@link Roo.MessageBox}
31721  */
31722 Roo.Msg = Roo.MessageBox;/*
31723  * Based on:
31724  * Ext JS Library 1.1.1
31725  * Copyright(c) 2006-2007, Ext JS, LLC.
31726  *
31727  * Originally Released Under LGPL - original licence link has changed is not relivant.
31728  *
31729  * Fork - LGPL
31730  * <script type="text/javascript">
31731  */
31732 /**
31733  * @class Roo.QuickTips
31734  * Provides attractive and customizable tooltips for any element.
31735  * @singleton
31736  */
31737 Roo.QuickTips = function(){
31738     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31739     var ce, bd, xy, dd;
31740     var visible = false, disabled = true, inited = false;
31741     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31742     
31743     var onOver = function(e){
31744         if(disabled){
31745             return;
31746         }
31747         var t = e.getTarget();
31748         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31749             return;
31750         }
31751         if(ce && t == ce.el){
31752             clearTimeout(hideProc);
31753             return;
31754         }
31755         if(t && tagEls[t.id]){
31756             tagEls[t.id].el = t;
31757             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31758             return;
31759         }
31760         var ttp, et = Roo.fly(t);
31761         var ns = cfg.namespace;
31762         if(tm.interceptTitles && t.title){
31763             ttp = t.title;
31764             t.qtip = ttp;
31765             t.removeAttribute("title");
31766             e.preventDefault();
31767         }else{
31768             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31769         }
31770         if(ttp){
31771             showProc = show.defer(tm.showDelay, tm, [{
31772                 el: t, 
31773                 text: ttp, 
31774                 width: et.getAttributeNS(ns, cfg.width),
31775                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31776                 title: et.getAttributeNS(ns, cfg.title),
31777                     cls: et.getAttributeNS(ns, cfg.cls)
31778             }]);
31779         }
31780     };
31781     
31782     var onOut = function(e){
31783         clearTimeout(showProc);
31784         var t = e.getTarget();
31785         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31786             hideProc = setTimeout(hide, tm.hideDelay);
31787         }
31788     };
31789     
31790     var onMove = function(e){
31791         if(disabled){
31792             return;
31793         }
31794         xy = e.getXY();
31795         xy[1] += 18;
31796         if(tm.trackMouse && ce){
31797             el.setXY(xy);
31798         }
31799     };
31800     
31801     var onDown = function(e){
31802         clearTimeout(showProc);
31803         clearTimeout(hideProc);
31804         if(!e.within(el)){
31805             if(tm.hideOnClick){
31806                 hide();
31807                 tm.disable();
31808                 tm.enable.defer(100, tm);
31809             }
31810         }
31811     };
31812     
31813     var getPad = function(){
31814         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31815     };
31816
31817     var show = function(o){
31818         if(disabled){
31819             return;
31820         }
31821         clearTimeout(dismissProc);
31822         ce = o;
31823         if(removeCls){ // in case manually hidden
31824             el.removeClass(removeCls);
31825             removeCls = null;
31826         }
31827         if(ce.cls){
31828             el.addClass(ce.cls);
31829             removeCls = ce.cls;
31830         }
31831         if(ce.title){
31832             tipTitle.update(ce.title);
31833             tipTitle.show();
31834         }else{
31835             tipTitle.update('');
31836             tipTitle.hide();
31837         }
31838         el.dom.style.width  = tm.maxWidth+'px';
31839         //tipBody.dom.style.width = '';
31840         tipBodyText.update(o.text);
31841         var p = getPad(), w = ce.width;
31842         if(!w){
31843             var td = tipBodyText.dom;
31844             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31845             if(aw > tm.maxWidth){
31846                 w = tm.maxWidth;
31847             }else if(aw < tm.minWidth){
31848                 w = tm.minWidth;
31849             }else{
31850                 w = aw;
31851             }
31852         }
31853         //tipBody.setWidth(w);
31854         el.setWidth(parseInt(w, 10) + p);
31855         if(ce.autoHide === false){
31856             close.setDisplayed(true);
31857             if(dd){
31858                 dd.unlock();
31859             }
31860         }else{
31861             close.setDisplayed(false);
31862             if(dd){
31863                 dd.lock();
31864             }
31865         }
31866         if(xy){
31867             el.avoidY = xy[1]-18;
31868             el.setXY(xy);
31869         }
31870         if(tm.animate){
31871             el.setOpacity(.1);
31872             el.setStyle("visibility", "visible");
31873             el.fadeIn({callback: afterShow});
31874         }else{
31875             afterShow();
31876         }
31877     };
31878     
31879     var afterShow = function(){
31880         if(ce){
31881             el.show();
31882             esc.enable();
31883             if(tm.autoDismiss && ce.autoHide !== false){
31884                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31885             }
31886         }
31887     };
31888     
31889     var hide = function(noanim){
31890         clearTimeout(dismissProc);
31891         clearTimeout(hideProc);
31892         ce = null;
31893         if(el.isVisible()){
31894             esc.disable();
31895             if(noanim !== true && tm.animate){
31896                 el.fadeOut({callback: afterHide});
31897             }else{
31898                 afterHide();
31899             } 
31900         }
31901     };
31902     
31903     var afterHide = function(){
31904         el.hide();
31905         if(removeCls){
31906             el.removeClass(removeCls);
31907             removeCls = null;
31908         }
31909     };
31910     
31911     return {
31912         /**
31913         * @cfg {Number} minWidth
31914         * The minimum width of the quick tip (defaults to 40)
31915         */
31916        minWidth : 40,
31917         /**
31918         * @cfg {Number} maxWidth
31919         * The maximum width of the quick tip (defaults to 300)
31920         */
31921        maxWidth : 300,
31922         /**
31923         * @cfg {Boolean} interceptTitles
31924         * True to automatically use the element's DOM title value if available (defaults to false)
31925         */
31926        interceptTitles : false,
31927         /**
31928         * @cfg {Boolean} trackMouse
31929         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31930         */
31931        trackMouse : false,
31932         /**
31933         * @cfg {Boolean} hideOnClick
31934         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31935         */
31936        hideOnClick : true,
31937         /**
31938         * @cfg {Number} showDelay
31939         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31940         */
31941        showDelay : 500,
31942         /**
31943         * @cfg {Number} hideDelay
31944         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31945         */
31946        hideDelay : 200,
31947         /**
31948         * @cfg {Boolean} autoHide
31949         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31950         * Used in conjunction with hideDelay.
31951         */
31952        autoHide : true,
31953         /**
31954         * @cfg {Boolean}
31955         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31956         * (defaults to true).  Used in conjunction with autoDismissDelay.
31957         */
31958        autoDismiss : true,
31959         /**
31960         * @cfg {Number}
31961         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31962         */
31963        autoDismissDelay : 5000,
31964        /**
31965         * @cfg {Boolean} animate
31966         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31967         */
31968        animate : false,
31969
31970        /**
31971         * @cfg {String} title
31972         * Title text to display (defaults to '').  This can be any valid HTML markup.
31973         */
31974         title: '',
31975        /**
31976         * @cfg {String} text
31977         * Body text to display (defaults to '').  This can be any valid HTML markup.
31978         */
31979         text : '',
31980        /**
31981         * @cfg {String} cls
31982         * A CSS class to apply to the base quick tip element (defaults to '').
31983         */
31984         cls : '',
31985        /**
31986         * @cfg {Number} width
31987         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31988         * minWidth or maxWidth.
31989         */
31990         width : null,
31991
31992     /**
31993      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31994      * or display QuickTips in a page.
31995      */
31996        init : function(){
31997           tm = Roo.QuickTips;
31998           cfg = tm.tagConfig;
31999           if(!inited){
32000               if(!Roo.isReady){ // allow calling of init() before onReady
32001                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32002                   return;
32003               }
32004               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32005               el.fxDefaults = {stopFx: true};
32006               // maximum custom styling
32007               //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>');
32008               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>');              
32009               tipTitle = el.child('h3');
32010               tipTitle.enableDisplayMode("block");
32011               tipBody = el.child('div.x-tip-bd');
32012               tipBodyText = el.child('div.x-tip-bd-inner');
32013               //bdLeft = el.child('div.x-tip-bd-left');
32014               //bdRight = el.child('div.x-tip-bd-right');
32015               close = el.child('div.x-tip-close');
32016               close.enableDisplayMode("block");
32017               close.on("click", hide);
32018               var d = Roo.get(document);
32019               d.on("mousedown", onDown);
32020               d.on("mouseover", onOver);
32021               d.on("mouseout", onOut);
32022               d.on("mousemove", onMove);
32023               esc = d.addKeyListener(27, hide);
32024               esc.disable();
32025               if(Roo.dd.DD){
32026                   dd = el.initDD("default", null, {
32027                       onDrag : function(){
32028                           el.sync();  
32029                       }
32030                   });
32031                   dd.setHandleElId(tipTitle.id);
32032                   dd.lock();
32033               }
32034               inited = true;
32035           }
32036           this.enable(); 
32037        },
32038
32039     /**
32040      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32041      * are supported:
32042      * <pre>
32043 Property    Type                   Description
32044 ----------  ---------------------  ------------------------------------------------------------------------
32045 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32046      * </ul>
32047      * @param {Object} config The config object
32048      */
32049        register : function(config){
32050            var cs = config instanceof Array ? config : arguments;
32051            for(var i = 0, len = cs.length; i < len; i++) {
32052                var c = cs[i];
32053                var target = c.target;
32054                if(target){
32055                    if(target instanceof Array){
32056                        for(var j = 0, jlen = target.length; j < jlen; j++){
32057                            tagEls[target[j]] = c;
32058                        }
32059                    }else{
32060                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32061                    }
32062                }
32063            }
32064        },
32065
32066     /**
32067      * Removes this quick tip from its element and destroys it.
32068      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32069      */
32070        unregister : function(el){
32071            delete tagEls[Roo.id(el)];
32072        },
32073
32074     /**
32075      * Enable this quick tip.
32076      */
32077        enable : function(){
32078            if(inited && disabled){
32079                locks.pop();
32080                if(locks.length < 1){
32081                    disabled = false;
32082                }
32083            }
32084        },
32085
32086     /**
32087      * Disable this quick tip.
32088      */
32089        disable : function(){
32090           disabled = true;
32091           clearTimeout(showProc);
32092           clearTimeout(hideProc);
32093           clearTimeout(dismissProc);
32094           if(ce){
32095               hide(true);
32096           }
32097           locks.push(1);
32098        },
32099
32100     /**
32101      * Returns true if the quick tip is enabled, else false.
32102      */
32103        isEnabled : function(){
32104             return !disabled;
32105        },
32106
32107         // private
32108        tagConfig : {
32109            namespace : "ext",
32110            attribute : "qtip",
32111            width : "width",
32112            target : "target",
32113            title : "qtitle",
32114            hide : "hide",
32115            cls : "qclass"
32116        }
32117    };
32118 }();
32119
32120 // backwards compat
32121 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32122  * Based on:
32123  * Ext JS Library 1.1.1
32124  * Copyright(c) 2006-2007, Ext JS, LLC.
32125  *
32126  * Originally Released Under LGPL - original licence link has changed is not relivant.
32127  *
32128  * Fork - LGPL
32129  * <script type="text/javascript">
32130  */
32131  
32132
32133 /**
32134  * @class Roo.tree.TreePanel
32135  * @extends Roo.data.Tree
32136
32137  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32138  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32139  * @cfg {Boolean} enableDD true to enable drag and drop
32140  * @cfg {Boolean} enableDrag true to enable just drag
32141  * @cfg {Boolean} enableDrop true to enable just drop
32142  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32143  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32144  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32145  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32146  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32147  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32148  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32149  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32150  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32151  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32152  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32153  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32154  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32155  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32156  * @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>
32157  * @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>
32158  * 
32159  * @constructor
32160  * @param {String/HTMLElement/Element} el The container element
32161  * @param {Object} config
32162  */
32163 Roo.tree.TreePanel = function(el, config){
32164     var root = false;
32165     var loader = false;
32166     if (config.root) {
32167         root = config.root;
32168         delete config.root;
32169     }
32170     if (config.loader) {
32171         loader = config.loader;
32172         delete config.loader;
32173     }
32174     
32175     Roo.apply(this, config);
32176     Roo.tree.TreePanel.superclass.constructor.call(this);
32177     this.el = Roo.get(el);
32178     this.el.addClass('x-tree');
32179     //console.log(root);
32180     if (root) {
32181         this.setRootNode( Roo.factory(root, Roo.tree));
32182     }
32183     if (loader) {
32184         this.loader = Roo.factory(loader, Roo.tree);
32185     }
32186    /**
32187     * Read-only. The id of the container element becomes this TreePanel's id.
32188     */
32189     this.id = this.el.id;
32190     this.addEvents({
32191         /**
32192         * @event beforeload
32193         * Fires before a node is loaded, return false to cancel
32194         * @param {Node} node The node being loaded
32195         */
32196         "beforeload" : true,
32197         /**
32198         * @event load
32199         * Fires when a node is loaded
32200         * @param {Node} node The node that was loaded
32201         */
32202         "load" : true,
32203         /**
32204         * @event textchange
32205         * Fires when the text for a node is changed
32206         * @param {Node} node The node
32207         * @param {String} text The new text
32208         * @param {String} oldText The old text
32209         */
32210         "textchange" : true,
32211         /**
32212         * @event beforeexpand
32213         * Fires before a node is expanded, return false to cancel.
32214         * @param {Node} node The node
32215         * @param {Boolean} deep
32216         * @param {Boolean} anim
32217         */
32218         "beforeexpand" : true,
32219         /**
32220         * @event beforecollapse
32221         * Fires before a node is collapsed, return false to cancel.
32222         * @param {Node} node The node
32223         * @param {Boolean} deep
32224         * @param {Boolean} anim
32225         */
32226         "beforecollapse" : true,
32227         /**
32228         * @event expand
32229         * Fires when a node is expanded
32230         * @param {Node} node The node
32231         */
32232         "expand" : true,
32233         /**
32234         * @event disabledchange
32235         * Fires when the disabled status of a node changes
32236         * @param {Node} node The node
32237         * @param {Boolean} disabled
32238         */
32239         "disabledchange" : true,
32240         /**
32241         * @event collapse
32242         * Fires when a node is collapsed
32243         * @param {Node} node The node
32244         */
32245         "collapse" : true,
32246         /**
32247         * @event beforeclick
32248         * Fires before click processing on a node. Return false to cancel the default action.
32249         * @param {Node} node The node
32250         * @param {Roo.EventObject} e The event object
32251         */
32252         "beforeclick":true,
32253         /**
32254         * @event checkchange
32255         * Fires when a node with a checkbox's checked property changes
32256         * @param {Node} this This node
32257         * @param {Boolean} checked
32258         */
32259         "checkchange":true,
32260         /**
32261         * @event click
32262         * Fires when a node is clicked
32263         * @param {Node} node The node
32264         * @param {Roo.EventObject} e The event object
32265         */
32266         "click":true,
32267         /**
32268         * @event dblclick
32269         * Fires when a node is double clicked
32270         * @param {Node} node The node
32271         * @param {Roo.EventObject} e The event object
32272         */
32273         "dblclick":true,
32274         /**
32275         * @event contextmenu
32276         * Fires when a node is right clicked
32277         * @param {Node} node The node
32278         * @param {Roo.EventObject} e The event object
32279         */
32280         "contextmenu":true,
32281         /**
32282         * @event beforechildrenrendered
32283         * Fires right before the child nodes for a node are rendered
32284         * @param {Node} node The node
32285         */
32286         "beforechildrenrendered":true,
32287         /**
32288         * @event startdrag
32289         * Fires when a node starts being dragged
32290         * @param {Roo.tree.TreePanel} this
32291         * @param {Roo.tree.TreeNode} node
32292         * @param {event} e The raw browser event
32293         */ 
32294        "startdrag" : true,
32295        /**
32296         * @event enddrag
32297         * Fires when a drag operation is complete
32298         * @param {Roo.tree.TreePanel} this
32299         * @param {Roo.tree.TreeNode} node
32300         * @param {event} e The raw browser event
32301         */
32302        "enddrag" : true,
32303        /**
32304         * @event dragdrop
32305         * Fires when a dragged node is dropped on a valid DD target
32306         * @param {Roo.tree.TreePanel} this
32307         * @param {Roo.tree.TreeNode} node
32308         * @param {DD} dd The dd it was dropped on
32309         * @param {event} e The raw browser event
32310         */
32311        "dragdrop" : true,
32312        /**
32313         * @event beforenodedrop
32314         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32315         * passed to handlers has the following properties:<br />
32316         * <ul style="padding:5px;padding-left:16px;">
32317         * <li>tree - The TreePanel</li>
32318         * <li>target - The node being targeted for the drop</li>
32319         * <li>data - The drag data from the drag source</li>
32320         * <li>point - The point of the drop - append, above or below</li>
32321         * <li>source - The drag source</li>
32322         * <li>rawEvent - Raw mouse event</li>
32323         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32324         * to be inserted by setting them on this object.</li>
32325         * <li>cancel - Set this to true to cancel the drop.</li>
32326         * </ul>
32327         * @param {Object} dropEvent
32328         */
32329        "beforenodedrop" : true,
32330        /**
32331         * @event nodedrop
32332         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32333         * passed to handlers has the following properties:<br />
32334         * <ul style="padding:5px;padding-left:16px;">
32335         * <li>tree - The TreePanel</li>
32336         * <li>target - The node being targeted for the drop</li>
32337         * <li>data - The drag data from the drag source</li>
32338         * <li>point - The point of the drop - append, above or below</li>
32339         * <li>source - The drag source</li>
32340         * <li>rawEvent - Raw mouse event</li>
32341         * <li>dropNode - Dropped node(s).</li>
32342         * </ul>
32343         * @param {Object} dropEvent
32344         */
32345        "nodedrop" : true,
32346         /**
32347         * @event nodedragover
32348         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32349         * passed to handlers has the following properties:<br />
32350         * <ul style="padding:5px;padding-left:16px;">
32351         * <li>tree - The TreePanel</li>
32352         * <li>target - The node being targeted for the drop</li>
32353         * <li>data - The drag data from the drag source</li>
32354         * <li>point - The point of the drop - append, above or below</li>
32355         * <li>source - The drag source</li>
32356         * <li>rawEvent - Raw mouse event</li>
32357         * <li>dropNode - Drop node(s) provided by the source.</li>
32358         * <li>cancel - Set this to true to signal drop not allowed.</li>
32359         * </ul>
32360         * @param {Object} dragOverEvent
32361         */
32362        "nodedragover" : true
32363         
32364     });
32365     if(this.singleExpand){
32366        this.on("beforeexpand", this.restrictExpand, this);
32367     }
32368     if (this.editor) {
32369         this.editor.tree = this;
32370         this.editor = Roo.factory(this.editor, Roo.tree);
32371     }
32372     
32373     if (this.selModel) {
32374         this.selModel = Roo.factory(this.selModel, Roo.tree);
32375     }
32376    
32377 };
32378 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32379     rootVisible : true,
32380     animate: Roo.enableFx,
32381     lines : true,
32382     enableDD : false,
32383     hlDrop : Roo.enableFx,
32384   
32385     renderer: false,
32386     
32387     rendererTip: false,
32388     // private
32389     restrictExpand : function(node){
32390         var p = node.parentNode;
32391         if(p){
32392             if(p.expandedChild && p.expandedChild.parentNode == p){
32393                 p.expandedChild.collapse();
32394             }
32395             p.expandedChild = node;
32396         }
32397     },
32398
32399     // private override
32400     setRootNode : function(node){
32401         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32402         if(!this.rootVisible){
32403             node.ui = new Roo.tree.RootTreeNodeUI(node);
32404         }
32405         return node;
32406     },
32407
32408     /**
32409      * Returns the container element for this TreePanel
32410      */
32411     getEl : function(){
32412         return this.el;
32413     },
32414
32415     /**
32416      * Returns the default TreeLoader for this TreePanel
32417      */
32418     getLoader : function(){
32419         return this.loader;
32420     },
32421
32422     /**
32423      * Expand all nodes
32424      */
32425     expandAll : function(){
32426         this.root.expand(true);
32427     },
32428
32429     /**
32430      * Collapse all nodes
32431      */
32432     collapseAll : function(){
32433         this.root.collapse(true);
32434     },
32435
32436     /**
32437      * Returns the selection model used by this TreePanel
32438      */
32439     getSelectionModel : function(){
32440         if(!this.selModel){
32441             this.selModel = new Roo.tree.DefaultSelectionModel();
32442         }
32443         return this.selModel;
32444     },
32445
32446     /**
32447      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32448      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32449      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32450      * @return {Array}
32451      */
32452     getChecked : function(a, startNode){
32453         startNode = startNode || this.root;
32454         var r = [];
32455         var f = function(){
32456             if(this.attributes.checked){
32457                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32458             }
32459         }
32460         startNode.cascade(f);
32461         return r;
32462     },
32463
32464     /**
32465      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32466      * @param {String} path
32467      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32468      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32469      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32470      */
32471     expandPath : function(path, attr, callback){
32472         attr = attr || "id";
32473         var keys = path.split(this.pathSeparator);
32474         var curNode = this.root;
32475         if(curNode.attributes[attr] != keys[1]){ // invalid root
32476             if(callback){
32477                 callback(false, null);
32478             }
32479             return;
32480         }
32481         var index = 1;
32482         var f = function(){
32483             if(++index == keys.length){
32484                 if(callback){
32485                     callback(true, curNode);
32486                 }
32487                 return;
32488             }
32489             var c = curNode.findChild(attr, keys[index]);
32490             if(!c){
32491                 if(callback){
32492                     callback(false, curNode);
32493                 }
32494                 return;
32495             }
32496             curNode = c;
32497             c.expand(false, false, f);
32498         };
32499         curNode.expand(false, false, f);
32500     },
32501
32502     /**
32503      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32504      * @param {String} path
32505      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32506      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32507      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32508      */
32509     selectPath : function(path, attr, callback){
32510         attr = attr || "id";
32511         var keys = path.split(this.pathSeparator);
32512         var v = keys.pop();
32513         if(keys.length > 0){
32514             var f = function(success, node){
32515                 if(success && node){
32516                     var n = node.findChild(attr, v);
32517                     if(n){
32518                         n.select();
32519                         if(callback){
32520                             callback(true, n);
32521                         }
32522                     }else if(callback){
32523                         callback(false, n);
32524                     }
32525                 }else{
32526                     if(callback){
32527                         callback(false, n);
32528                     }
32529                 }
32530             };
32531             this.expandPath(keys.join(this.pathSeparator), attr, f);
32532         }else{
32533             this.root.select();
32534             if(callback){
32535                 callback(true, this.root);
32536             }
32537         }
32538     },
32539
32540     getTreeEl : function(){
32541         return this.el;
32542     },
32543
32544     /**
32545      * Trigger rendering of this TreePanel
32546      */
32547     render : function(){
32548         if (this.innerCt) {
32549             return this; // stop it rendering more than once!!
32550         }
32551         
32552         this.innerCt = this.el.createChild({tag:"ul",
32553                cls:"x-tree-root-ct " +
32554                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32555
32556         if(this.containerScroll){
32557             Roo.dd.ScrollManager.register(this.el);
32558         }
32559         if((this.enableDD || this.enableDrop) && !this.dropZone){
32560            /**
32561             * The dropZone used by this tree if drop is enabled
32562             * @type Roo.tree.TreeDropZone
32563             */
32564              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32565                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32566            });
32567         }
32568         if((this.enableDD || this.enableDrag) && !this.dragZone){
32569            /**
32570             * The dragZone used by this tree if drag is enabled
32571             * @type Roo.tree.TreeDragZone
32572             */
32573             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32574                ddGroup: this.ddGroup || "TreeDD",
32575                scroll: this.ddScroll
32576            });
32577         }
32578         this.getSelectionModel().init(this);
32579         if (!this.root) {
32580             Roo.log("ROOT not set in tree");
32581             return this;
32582         }
32583         this.root.render();
32584         if(!this.rootVisible){
32585             this.root.renderChildren();
32586         }
32587         return this;
32588     }
32589 });/*
32590  * Based on:
32591  * Ext JS Library 1.1.1
32592  * Copyright(c) 2006-2007, Ext JS, LLC.
32593  *
32594  * Originally Released Under LGPL - original licence link has changed is not relivant.
32595  *
32596  * Fork - LGPL
32597  * <script type="text/javascript">
32598  */
32599  
32600
32601 /**
32602  * @class Roo.tree.DefaultSelectionModel
32603  * @extends Roo.util.Observable
32604  * The default single selection for a TreePanel.
32605  * @param {Object} cfg Configuration
32606  */
32607 Roo.tree.DefaultSelectionModel = function(cfg){
32608    this.selNode = null;
32609    
32610    
32611    
32612    this.addEvents({
32613        /**
32614         * @event selectionchange
32615         * Fires when the selected node changes
32616         * @param {DefaultSelectionModel} this
32617         * @param {TreeNode} node the new selection
32618         */
32619        "selectionchange" : true,
32620
32621        /**
32622         * @event beforeselect
32623         * Fires before the selected node changes, return false to cancel the change
32624         * @param {DefaultSelectionModel} this
32625         * @param {TreeNode} node the new selection
32626         * @param {TreeNode} node the old selection
32627         */
32628        "beforeselect" : true
32629    });
32630    
32631     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32632 };
32633
32634 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32635     init : function(tree){
32636         this.tree = tree;
32637         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32638         tree.on("click", this.onNodeClick, this);
32639     },
32640     
32641     onNodeClick : function(node, e){
32642         if (e.ctrlKey && this.selNode == node)  {
32643             this.unselect(node);
32644             return;
32645         }
32646         this.select(node);
32647     },
32648     
32649     /**
32650      * Select a node.
32651      * @param {TreeNode} node The node to select
32652      * @return {TreeNode} The selected node
32653      */
32654     select : function(node){
32655         var last = this.selNode;
32656         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32657             if(last){
32658                 last.ui.onSelectedChange(false);
32659             }
32660             this.selNode = node;
32661             node.ui.onSelectedChange(true);
32662             this.fireEvent("selectionchange", this, node, last);
32663         }
32664         return node;
32665     },
32666     
32667     /**
32668      * Deselect a node.
32669      * @param {TreeNode} node The node to unselect
32670      */
32671     unselect : function(node){
32672         if(this.selNode == node){
32673             this.clearSelections();
32674         }    
32675     },
32676     
32677     /**
32678      * Clear all selections
32679      */
32680     clearSelections : function(){
32681         var n = this.selNode;
32682         if(n){
32683             n.ui.onSelectedChange(false);
32684             this.selNode = null;
32685             this.fireEvent("selectionchange", this, null);
32686         }
32687         return n;
32688     },
32689     
32690     /**
32691      * Get the selected node
32692      * @return {TreeNode} The selected node
32693      */
32694     getSelectedNode : function(){
32695         return this.selNode;    
32696     },
32697     
32698     /**
32699      * Returns true if the node is selected
32700      * @param {TreeNode} node The node to check
32701      * @return {Boolean}
32702      */
32703     isSelected : function(node){
32704         return this.selNode == node;  
32705     },
32706
32707     /**
32708      * Selects the node above the selected node in the tree, intelligently walking the nodes
32709      * @return TreeNode The new selection
32710      */
32711     selectPrevious : function(){
32712         var s = this.selNode || this.lastSelNode;
32713         if(!s){
32714             return null;
32715         }
32716         var ps = s.previousSibling;
32717         if(ps){
32718             if(!ps.isExpanded() || ps.childNodes.length < 1){
32719                 return this.select(ps);
32720             } else{
32721                 var lc = ps.lastChild;
32722                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32723                     lc = lc.lastChild;
32724                 }
32725                 return this.select(lc);
32726             }
32727         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32728             return this.select(s.parentNode);
32729         }
32730         return null;
32731     },
32732
32733     /**
32734      * Selects the node above the selected node in the tree, intelligently walking the nodes
32735      * @return TreeNode The new selection
32736      */
32737     selectNext : function(){
32738         var s = this.selNode || this.lastSelNode;
32739         if(!s){
32740             return null;
32741         }
32742         if(s.firstChild && s.isExpanded()){
32743              return this.select(s.firstChild);
32744          }else if(s.nextSibling){
32745              return this.select(s.nextSibling);
32746          }else if(s.parentNode){
32747             var newS = null;
32748             s.parentNode.bubble(function(){
32749                 if(this.nextSibling){
32750                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32751                     return false;
32752                 }
32753             });
32754             return newS;
32755          }
32756         return null;
32757     },
32758
32759     onKeyDown : function(e){
32760         var s = this.selNode || this.lastSelNode;
32761         // undesirable, but required
32762         var sm = this;
32763         if(!s){
32764             return;
32765         }
32766         var k = e.getKey();
32767         switch(k){
32768              case e.DOWN:
32769                  e.stopEvent();
32770                  this.selectNext();
32771              break;
32772              case e.UP:
32773                  e.stopEvent();
32774                  this.selectPrevious();
32775              break;
32776              case e.RIGHT:
32777                  e.preventDefault();
32778                  if(s.hasChildNodes()){
32779                      if(!s.isExpanded()){
32780                          s.expand();
32781                      }else if(s.firstChild){
32782                          this.select(s.firstChild, e);
32783                      }
32784                  }
32785              break;
32786              case e.LEFT:
32787                  e.preventDefault();
32788                  if(s.hasChildNodes() && s.isExpanded()){
32789                      s.collapse();
32790                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32791                      this.select(s.parentNode, e);
32792                  }
32793              break;
32794         };
32795     }
32796 });
32797
32798 /**
32799  * @class Roo.tree.MultiSelectionModel
32800  * @extends Roo.util.Observable
32801  * Multi selection for a TreePanel.
32802  * @param {Object} cfg Configuration
32803  */
32804 Roo.tree.MultiSelectionModel = function(){
32805    this.selNodes = [];
32806    this.selMap = {};
32807    this.addEvents({
32808        /**
32809         * @event selectionchange
32810         * Fires when the selected nodes change
32811         * @param {MultiSelectionModel} this
32812         * @param {Array} nodes Array of the selected nodes
32813         */
32814        "selectionchange" : true
32815    });
32816    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32817    
32818 };
32819
32820 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32821     init : function(tree){
32822         this.tree = tree;
32823         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32824         tree.on("click", this.onNodeClick, this);
32825     },
32826     
32827     onNodeClick : function(node, e){
32828         this.select(node, e, e.ctrlKey);
32829     },
32830     
32831     /**
32832      * Select a node.
32833      * @param {TreeNode} node The node to select
32834      * @param {EventObject} e (optional) An event associated with the selection
32835      * @param {Boolean} keepExisting True to retain existing selections
32836      * @return {TreeNode} The selected node
32837      */
32838     select : function(node, e, keepExisting){
32839         if(keepExisting !== true){
32840             this.clearSelections(true);
32841         }
32842         if(this.isSelected(node)){
32843             this.lastSelNode = node;
32844             return node;
32845         }
32846         this.selNodes.push(node);
32847         this.selMap[node.id] = node;
32848         this.lastSelNode = node;
32849         node.ui.onSelectedChange(true);
32850         this.fireEvent("selectionchange", this, this.selNodes);
32851         return node;
32852     },
32853     
32854     /**
32855      * Deselect a node.
32856      * @param {TreeNode} node The node to unselect
32857      */
32858     unselect : function(node){
32859         if(this.selMap[node.id]){
32860             node.ui.onSelectedChange(false);
32861             var sn = this.selNodes;
32862             var index = -1;
32863             if(sn.indexOf){
32864                 index = sn.indexOf(node);
32865             }else{
32866                 for(var i = 0, len = sn.length; i < len; i++){
32867                     if(sn[i] == node){
32868                         index = i;
32869                         break;
32870                     }
32871                 }
32872             }
32873             if(index != -1){
32874                 this.selNodes.splice(index, 1);
32875             }
32876             delete this.selMap[node.id];
32877             this.fireEvent("selectionchange", this, this.selNodes);
32878         }
32879     },
32880     
32881     /**
32882      * Clear all selections
32883      */
32884     clearSelections : function(suppressEvent){
32885         var sn = this.selNodes;
32886         if(sn.length > 0){
32887             for(var i = 0, len = sn.length; i < len; i++){
32888                 sn[i].ui.onSelectedChange(false);
32889             }
32890             this.selNodes = [];
32891             this.selMap = {};
32892             if(suppressEvent !== true){
32893                 this.fireEvent("selectionchange", this, this.selNodes);
32894             }
32895         }
32896     },
32897     
32898     /**
32899      * Returns true if the node is selected
32900      * @param {TreeNode} node The node to check
32901      * @return {Boolean}
32902      */
32903     isSelected : function(node){
32904         return this.selMap[node.id] ? true : false;  
32905     },
32906     
32907     /**
32908      * Returns an array of the selected nodes
32909      * @return {Array}
32910      */
32911     getSelectedNodes : function(){
32912         return this.selNodes;    
32913     },
32914
32915     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32916
32917     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32918
32919     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32920 });/*
32921  * Based on:
32922  * Ext JS Library 1.1.1
32923  * Copyright(c) 2006-2007, Ext JS, LLC.
32924  *
32925  * Originally Released Under LGPL - original licence link has changed is not relivant.
32926  *
32927  * Fork - LGPL
32928  * <script type="text/javascript">
32929  */
32930  
32931 /**
32932  * @class Roo.tree.TreeNode
32933  * @extends Roo.data.Node
32934  * @cfg {String} text The text for this node
32935  * @cfg {Boolean} expanded true to start the node expanded
32936  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32937  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32938  * @cfg {Boolean} disabled true to start the node disabled
32939  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32940  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32941  * @cfg {String} cls A css class to be added to the node
32942  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32943  * @cfg {String} href URL of the link used for the node (defaults to #)
32944  * @cfg {String} hrefTarget target frame for the link
32945  * @cfg {String} qtip An Ext QuickTip for the node
32946  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32947  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32948  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32949  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32950  * (defaults to undefined with no checkbox rendered)
32951  * @constructor
32952  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32953  */
32954 Roo.tree.TreeNode = function(attributes){
32955     attributes = attributes || {};
32956     if(typeof attributes == "string"){
32957         attributes = {text: attributes};
32958     }
32959     this.childrenRendered = false;
32960     this.rendered = false;
32961     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32962     this.expanded = attributes.expanded === true;
32963     this.isTarget = attributes.isTarget !== false;
32964     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32965     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32966
32967     /**
32968      * Read-only. The text for this node. To change it use setText().
32969      * @type String
32970      */
32971     this.text = attributes.text;
32972     /**
32973      * True if this node is disabled.
32974      * @type Boolean
32975      */
32976     this.disabled = attributes.disabled === true;
32977
32978     this.addEvents({
32979         /**
32980         * @event textchange
32981         * Fires when the text for this node is changed
32982         * @param {Node} this This node
32983         * @param {String} text The new text
32984         * @param {String} oldText The old text
32985         */
32986         "textchange" : true,
32987         /**
32988         * @event beforeexpand
32989         * Fires before this node is expanded, return false to cancel.
32990         * @param {Node} this This node
32991         * @param {Boolean} deep
32992         * @param {Boolean} anim
32993         */
32994         "beforeexpand" : true,
32995         /**
32996         * @event beforecollapse
32997         * Fires before this node is collapsed, return false to cancel.
32998         * @param {Node} this This node
32999         * @param {Boolean} deep
33000         * @param {Boolean} anim
33001         */
33002         "beforecollapse" : true,
33003         /**
33004         * @event expand
33005         * Fires when this node is expanded
33006         * @param {Node} this This node
33007         */
33008         "expand" : true,
33009         /**
33010         * @event disabledchange
33011         * Fires when the disabled status of this node changes
33012         * @param {Node} this This node
33013         * @param {Boolean} disabled
33014         */
33015         "disabledchange" : true,
33016         /**
33017         * @event collapse
33018         * Fires when this node is collapsed
33019         * @param {Node} this This node
33020         */
33021         "collapse" : true,
33022         /**
33023         * @event beforeclick
33024         * Fires before click processing. Return false to cancel the default action.
33025         * @param {Node} this This node
33026         * @param {Roo.EventObject} e The event object
33027         */
33028         "beforeclick":true,
33029         /**
33030         * @event checkchange
33031         * Fires when a node with a checkbox's checked property changes
33032         * @param {Node} this This node
33033         * @param {Boolean} checked
33034         */
33035         "checkchange":true,
33036         /**
33037         * @event click
33038         * Fires when this node is clicked
33039         * @param {Node} this This node
33040         * @param {Roo.EventObject} e The event object
33041         */
33042         "click":true,
33043         /**
33044         * @event dblclick
33045         * Fires when this node is double clicked
33046         * @param {Node} this This node
33047         * @param {Roo.EventObject} e The event object
33048         */
33049         "dblclick":true,
33050         /**
33051         * @event contextmenu
33052         * Fires when this node is right clicked
33053         * @param {Node} this This node
33054         * @param {Roo.EventObject} e The event object
33055         */
33056         "contextmenu":true,
33057         /**
33058         * @event beforechildrenrendered
33059         * Fires right before the child nodes for this node are rendered
33060         * @param {Node} this This node
33061         */
33062         "beforechildrenrendered":true
33063     });
33064
33065     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33066
33067     /**
33068      * Read-only. The UI for this node
33069      * @type TreeNodeUI
33070      */
33071     this.ui = new uiClass(this);
33072     
33073     // finally support items[]
33074     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33075         return;
33076     }
33077     
33078     
33079     Roo.each(this.attributes.items, function(c) {
33080         this.appendChild(Roo.factory(c,Roo.Tree));
33081     }, this);
33082     delete this.attributes.items;
33083     
33084     
33085     
33086 };
33087 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33088     preventHScroll: true,
33089     /**
33090      * Returns true if this node is expanded
33091      * @return {Boolean}
33092      */
33093     isExpanded : function(){
33094         return this.expanded;
33095     },
33096
33097     /**
33098      * Returns the UI object for this node
33099      * @return {TreeNodeUI}
33100      */
33101     getUI : function(){
33102         return this.ui;
33103     },
33104
33105     // private override
33106     setFirstChild : function(node){
33107         var of = this.firstChild;
33108         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33109         if(this.childrenRendered && of && node != of){
33110             of.renderIndent(true, true);
33111         }
33112         if(this.rendered){
33113             this.renderIndent(true, true);
33114         }
33115     },
33116
33117     // private override
33118     setLastChild : function(node){
33119         var ol = this.lastChild;
33120         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33121         if(this.childrenRendered && ol && node != ol){
33122             ol.renderIndent(true, true);
33123         }
33124         if(this.rendered){
33125             this.renderIndent(true, true);
33126         }
33127     },
33128
33129     // these methods are overridden to provide lazy rendering support
33130     // private override
33131     appendChild : function()
33132     {
33133         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33134         if(node && this.childrenRendered){
33135             node.render();
33136         }
33137         this.ui.updateExpandIcon();
33138         return node;
33139     },
33140
33141     // private override
33142     removeChild : function(node){
33143         this.ownerTree.getSelectionModel().unselect(node);
33144         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33145         // if it's been rendered remove dom node
33146         if(this.childrenRendered){
33147             node.ui.remove();
33148         }
33149         if(this.childNodes.length < 1){
33150             this.collapse(false, false);
33151         }else{
33152             this.ui.updateExpandIcon();
33153         }
33154         if(!this.firstChild) {
33155             this.childrenRendered = false;
33156         }
33157         return node;
33158     },
33159
33160     // private override
33161     insertBefore : function(node, refNode){
33162         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33163         if(newNode && refNode && this.childrenRendered){
33164             node.render();
33165         }
33166         this.ui.updateExpandIcon();
33167         return newNode;
33168     },
33169
33170     /**
33171      * Sets the text for this node
33172      * @param {String} text
33173      */
33174     setText : function(text){
33175         var oldText = this.text;
33176         this.text = text;
33177         this.attributes.text = text;
33178         if(this.rendered){ // event without subscribing
33179             this.ui.onTextChange(this, text, oldText);
33180         }
33181         this.fireEvent("textchange", this, text, oldText);
33182     },
33183
33184     /**
33185      * Triggers selection of this node
33186      */
33187     select : function(){
33188         this.getOwnerTree().getSelectionModel().select(this);
33189     },
33190
33191     /**
33192      * Triggers deselection of this node
33193      */
33194     unselect : function(){
33195         this.getOwnerTree().getSelectionModel().unselect(this);
33196     },
33197
33198     /**
33199      * Returns true if this node is selected
33200      * @return {Boolean}
33201      */
33202     isSelected : function(){
33203         return this.getOwnerTree().getSelectionModel().isSelected(this);
33204     },
33205
33206     /**
33207      * Expand this node.
33208      * @param {Boolean} deep (optional) True to expand all children as well
33209      * @param {Boolean} anim (optional) false to cancel the default animation
33210      * @param {Function} callback (optional) A callback to be called when
33211      * expanding this node completes (does not wait for deep expand to complete).
33212      * Called with 1 parameter, this node.
33213      */
33214     expand : function(deep, anim, callback){
33215         if(!this.expanded){
33216             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33217                 return;
33218             }
33219             if(!this.childrenRendered){
33220                 this.renderChildren();
33221             }
33222             this.expanded = true;
33223             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33224                 this.ui.animExpand(function(){
33225                     this.fireEvent("expand", this);
33226                     if(typeof callback == "function"){
33227                         callback(this);
33228                     }
33229                     if(deep === true){
33230                         this.expandChildNodes(true);
33231                     }
33232                 }.createDelegate(this));
33233                 return;
33234             }else{
33235                 this.ui.expand();
33236                 this.fireEvent("expand", this);
33237                 if(typeof callback == "function"){
33238                     callback(this);
33239                 }
33240             }
33241         }else{
33242            if(typeof callback == "function"){
33243                callback(this);
33244            }
33245         }
33246         if(deep === true){
33247             this.expandChildNodes(true);
33248         }
33249     },
33250
33251     isHiddenRoot : function(){
33252         return this.isRoot && !this.getOwnerTree().rootVisible;
33253     },
33254
33255     /**
33256      * Collapse this node.
33257      * @param {Boolean} deep (optional) True to collapse all children as well
33258      * @param {Boolean} anim (optional) false to cancel the default animation
33259      */
33260     collapse : function(deep, anim){
33261         if(this.expanded && !this.isHiddenRoot()){
33262             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33263                 return;
33264             }
33265             this.expanded = false;
33266             if((this.getOwnerTree().animate && anim !== false) || anim){
33267                 this.ui.animCollapse(function(){
33268                     this.fireEvent("collapse", this);
33269                     if(deep === true){
33270                         this.collapseChildNodes(true);
33271                     }
33272                 }.createDelegate(this));
33273                 return;
33274             }else{
33275                 this.ui.collapse();
33276                 this.fireEvent("collapse", this);
33277             }
33278         }
33279         if(deep === true){
33280             var cs = this.childNodes;
33281             for(var i = 0, len = cs.length; i < len; i++) {
33282                 cs[i].collapse(true, false);
33283             }
33284         }
33285     },
33286
33287     // private
33288     delayedExpand : function(delay){
33289         if(!this.expandProcId){
33290             this.expandProcId = this.expand.defer(delay, this);
33291         }
33292     },
33293
33294     // private
33295     cancelExpand : function(){
33296         if(this.expandProcId){
33297             clearTimeout(this.expandProcId);
33298         }
33299         this.expandProcId = false;
33300     },
33301
33302     /**
33303      * Toggles expanded/collapsed state of the node
33304      */
33305     toggle : function(){
33306         if(this.expanded){
33307             this.collapse();
33308         }else{
33309             this.expand();
33310         }
33311     },
33312
33313     /**
33314      * Ensures all parent nodes are expanded
33315      */
33316     ensureVisible : function(callback){
33317         var tree = this.getOwnerTree();
33318         tree.expandPath(this.parentNode.getPath(), false, function(){
33319             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33320             Roo.callback(callback);
33321         }.createDelegate(this));
33322     },
33323
33324     /**
33325      * Expand all child nodes
33326      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33327      */
33328     expandChildNodes : function(deep){
33329         var cs = this.childNodes;
33330         for(var i = 0, len = cs.length; i < len; i++) {
33331                 cs[i].expand(deep);
33332         }
33333     },
33334
33335     /**
33336      * Collapse all child nodes
33337      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33338      */
33339     collapseChildNodes : function(deep){
33340         var cs = this.childNodes;
33341         for(var i = 0, len = cs.length; i < len; i++) {
33342                 cs[i].collapse(deep);
33343         }
33344     },
33345
33346     /**
33347      * Disables this node
33348      */
33349     disable : function(){
33350         this.disabled = true;
33351         this.unselect();
33352         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33353             this.ui.onDisableChange(this, true);
33354         }
33355         this.fireEvent("disabledchange", this, true);
33356     },
33357
33358     /**
33359      * Enables this node
33360      */
33361     enable : function(){
33362         this.disabled = false;
33363         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33364             this.ui.onDisableChange(this, false);
33365         }
33366         this.fireEvent("disabledchange", this, false);
33367     },
33368
33369     // private
33370     renderChildren : function(suppressEvent){
33371         if(suppressEvent !== false){
33372             this.fireEvent("beforechildrenrendered", this);
33373         }
33374         var cs = this.childNodes;
33375         for(var i = 0, len = cs.length; i < len; i++){
33376             cs[i].render(true);
33377         }
33378         this.childrenRendered = true;
33379     },
33380
33381     // private
33382     sort : function(fn, scope){
33383         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33384         if(this.childrenRendered){
33385             var cs = this.childNodes;
33386             for(var i = 0, len = cs.length; i < len; i++){
33387                 cs[i].render(true);
33388             }
33389         }
33390     },
33391
33392     // private
33393     render : function(bulkRender){
33394         this.ui.render(bulkRender);
33395         if(!this.rendered){
33396             this.rendered = true;
33397             if(this.expanded){
33398                 this.expanded = false;
33399                 this.expand(false, false);
33400             }
33401         }
33402     },
33403
33404     // private
33405     renderIndent : function(deep, refresh){
33406         if(refresh){
33407             this.ui.childIndent = null;
33408         }
33409         this.ui.renderIndent();
33410         if(deep === true && this.childrenRendered){
33411             var cs = this.childNodes;
33412             for(var i = 0, len = cs.length; i < len; i++){
33413                 cs[i].renderIndent(true, refresh);
33414             }
33415         }
33416     }
33417 });/*
33418  * Based on:
33419  * Ext JS Library 1.1.1
33420  * Copyright(c) 2006-2007, Ext JS, LLC.
33421  *
33422  * Originally Released Under LGPL - original licence link has changed is not relivant.
33423  *
33424  * Fork - LGPL
33425  * <script type="text/javascript">
33426  */
33427  
33428 /**
33429  * @class Roo.tree.AsyncTreeNode
33430  * @extends Roo.tree.TreeNode
33431  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33432  * @constructor
33433  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33434  */
33435  Roo.tree.AsyncTreeNode = function(config){
33436     this.loaded = false;
33437     this.loading = false;
33438     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33439     /**
33440     * @event beforeload
33441     * Fires before this node is loaded, return false to cancel
33442     * @param {Node} this This node
33443     */
33444     this.addEvents({'beforeload':true, 'load': true});
33445     /**
33446     * @event load
33447     * Fires when this node is loaded
33448     * @param {Node} this This node
33449     */
33450     /**
33451      * The loader used by this node (defaults to using the tree's defined loader)
33452      * @type TreeLoader
33453      * @property loader
33454      */
33455 };
33456 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33457     expand : function(deep, anim, callback){
33458         if(this.loading){ // if an async load is already running, waiting til it's done
33459             var timer;
33460             var f = function(){
33461                 if(!this.loading){ // done loading
33462                     clearInterval(timer);
33463                     this.expand(deep, anim, callback);
33464                 }
33465             }.createDelegate(this);
33466             timer = setInterval(f, 200);
33467             return;
33468         }
33469         if(!this.loaded){
33470             if(this.fireEvent("beforeload", this) === false){
33471                 return;
33472             }
33473             this.loading = true;
33474             this.ui.beforeLoad(this);
33475             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33476             if(loader){
33477                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33478                 return;
33479             }
33480         }
33481         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33482     },
33483     
33484     /**
33485      * Returns true if this node is currently loading
33486      * @return {Boolean}
33487      */
33488     isLoading : function(){
33489         return this.loading;  
33490     },
33491     
33492     loadComplete : function(deep, anim, callback){
33493         this.loading = false;
33494         this.loaded = true;
33495         this.ui.afterLoad(this);
33496         this.fireEvent("load", this);
33497         this.expand(deep, anim, callback);
33498     },
33499     
33500     /**
33501      * Returns true if this node has been loaded
33502      * @return {Boolean}
33503      */
33504     isLoaded : function(){
33505         return this.loaded;
33506     },
33507     
33508     hasChildNodes : function(){
33509         if(!this.isLeaf() && !this.loaded){
33510             return true;
33511         }else{
33512             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33513         }
33514     },
33515
33516     /**
33517      * Trigger a reload for this node
33518      * @param {Function} callback
33519      */
33520     reload : function(callback){
33521         this.collapse(false, false);
33522         while(this.firstChild){
33523             this.removeChild(this.firstChild);
33524         }
33525         this.childrenRendered = false;
33526         this.loaded = false;
33527         if(this.isHiddenRoot()){
33528             this.expanded = false;
33529         }
33530         this.expand(false, false, callback);
33531     }
33532 });/*
33533  * Based on:
33534  * Ext JS Library 1.1.1
33535  * Copyright(c) 2006-2007, Ext JS, LLC.
33536  *
33537  * Originally Released Under LGPL - original licence link has changed is not relivant.
33538  *
33539  * Fork - LGPL
33540  * <script type="text/javascript">
33541  */
33542  
33543 /**
33544  * @class Roo.tree.TreeNodeUI
33545  * @constructor
33546  * @param {Object} node The node to render
33547  * The TreeNode UI implementation is separate from the
33548  * tree implementation. Unless you are customizing the tree UI,
33549  * you should never have to use this directly.
33550  */
33551 Roo.tree.TreeNodeUI = function(node){
33552     this.node = node;
33553     this.rendered = false;
33554     this.animating = false;
33555     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33556 };
33557
33558 Roo.tree.TreeNodeUI.prototype = {
33559     removeChild : function(node){
33560         if(this.rendered){
33561             this.ctNode.removeChild(node.ui.getEl());
33562         }
33563     },
33564
33565     beforeLoad : function(){
33566          this.addClass("x-tree-node-loading");
33567     },
33568
33569     afterLoad : function(){
33570          this.removeClass("x-tree-node-loading");
33571     },
33572
33573     onTextChange : function(node, text, oldText){
33574         if(this.rendered){
33575             this.textNode.innerHTML = text;
33576         }
33577     },
33578
33579     onDisableChange : function(node, state){
33580         this.disabled = state;
33581         if(state){
33582             this.addClass("x-tree-node-disabled");
33583         }else{
33584             this.removeClass("x-tree-node-disabled");
33585         }
33586     },
33587
33588     onSelectedChange : function(state){
33589         if(state){
33590             this.focus();
33591             this.addClass("x-tree-selected");
33592         }else{
33593             //this.blur();
33594             this.removeClass("x-tree-selected");
33595         }
33596     },
33597
33598     onMove : function(tree, node, oldParent, newParent, index, refNode){
33599         this.childIndent = null;
33600         if(this.rendered){
33601             var targetNode = newParent.ui.getContainer();
33602             if(!targetNode){//target not rendered
33603                 this.holder = document.createElement("div");
33604                 this.holder.appendChild(this.wrap);
33605                 return;
33606             }
33607             var insertBefore = refNode ? refNode.ui.getEl() : null;
33608             if(insertBefore){
33609                 targetNode.insertBefore(this.wrap, insertBefore);
33610             }else{
33611                 targetNode.appendChild(this.wrap);
33612             }
33613             this.node.renderIndent(true);
33614         }
33615     },
33616
33617     addClass : function(cls){
33618         if(this.elNode){
33619             Roo.fly(this.elNode).addClass(cls);
33620         }
33621     },
33622
33623     removeClass : function(cls){
33624         if(this.elNode){
33625             Roo.fly(this.elNode).removeClass(cls);
33626         }
33627     },
33628
33629     remove : function(){
33630         if(this.rendered){
33631             this.holder = document.createElement("div");
33632             this.holder.appendChild(this.wrap);
33633         }
33634     },
33635
33636     fireEvent : function(){
33637         return this.node.fireEvent.apply(this.node, arguments);
33638     },
33639
33640     initEvents : function(){
33641         this.node.on("move", this.onMove, this);
33642         var E = Roo.EventManager;
33643         var a = this.anchor;
33644
33645         var el = Roo.fly(a, '_treeui');
33646
33647         if(Roo.isOpera){ // opera render bug ignores the CSS
33648             el.setStyle("text-decoration", "none");
33649         }
33650
33651         el.on("click", this.onClick, this);
33652         el.on("dblclick", this.onDblClick, this);
33653
33654         if(this.checkbox){
33655             Roo.EventManager.on(this.checkbox,
33656                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33657         }
33658
33659         el.on("contextmenu", this.onContextMenu, this);
33660
33661         var icon = Roo.fly(this.iconNode);
33662         icon.on("click", this.onClick, this);
33663         icon.on("dblclick", this.onDblClick, this);
33664         icon.on("contextmenu", this.onContextMenu, this);
33665         E.on(this.ecNode, "click", this.ecClick, this, true);
33666
33667         if(this.node.disabled){
33668             this.addClass("x-tree-node-disabled");
33669         }
33670         if(this.node.hidden){
33671             this.addClass("x-tree-node-disabled");
33672         }
33673         var ot = this.node.getOwnerTree();
33674         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33675         if(dd && (!this.node.isRoot || ot.rootVisible)){
33676             Roo.dd.Registry.register(this.elNode, {
33677                 node: this.node,
33678                 handles: this.getDDHandles(),
33679                 isHandle: false
33680             });
33681         }
33682     },
33683
33684     getDDHandles : function(){
33685         return [this.iconNode, this.textNode];
33686     },
33687
33688     hide : function(){
33689         if(this.rendered){
33690             this.wrap.style.display = "none";
33691         }
33692     },
33693
33694     show : function(){
33695         if(this.rendered){
33696             this.wrap.style.display = "";
33697         }
33698     },
33699
33700     onContextMenu : function(e){
33701         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33702             e.preventDefault();
33703             this.focus();
33704             this.fireEvent("contextmenu", this.node, e);
33705         }
33706     },
33707
33708     onClick : function(e){
33709         if(this.dropping){
33710             e.stopEvent();
33711             return;
33712         }
33713         if(this.fireEvent("beforeclick", this.node, e) !== false){
33714             if(!this.disabled && this.node.attributes.href){
33715                 this.fireEvent("click", this.node, e);
33716                 return;
33717             }
33718             e.preventDefault();
33719             if(this.disabled){
33720                 return;
33721             }
33722
33723             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33724                 this.node.toggle();
33725             }
33726
33727             this.fireEvent("click", this.node, e);
33728         }else{
33729             e.stopEvent();
33730         }
33731     },
33732
33733     onDblClick : function(e){
33734         e.preventDefault();
33735         if(this.disabled){
33736             return;
33737         }
33738         if(this.checkbox){
33739             this.toggleCheck();
33740         }
33741         if(!this.animating && this.node.hasChildNodes()){
33742             this.node.toggle();
33743         }
33744         this.fireEvent("dblclick", this.node, e);
33745     },
33746
33747     onCheckChange : function(){
33748         var checked = this.checkbox.checked;
33749         this.node.attributes.checked = checked;
33750         this.fireEvent('checkchange', this.node, checked);
33751     },
33752
33753     ecClick : function(e){
33754         if(!this.animating && this.node.hasChildNodes()){
33755             this.node.toggle();
33756         }
33757     },
33758
33759     startDrop : function(){
33760         this.dropping = true;
33761     },
33762
33763     // delayed drop so the click event doesn't get fired on a drop
33764     endDrop : function(){
33765        setTimeout(function(){
33766            this.dropping = false;
33767        }.createDelegate(this), 50);
33768     },
33769
33770     expand : function(){
33771         this.updateExpandIcon();
33772         this.ctNode.style.display = "";
33773     },
33774
33775     focus : function(){
33776         if(!this.node.preventHScroll){
33777             try{this.anchor.focus();
33778             }catch(e){}
33779         }else if(!Roo.isIE){
33780             try{
33781                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33782                 var l = noscroll.scrollLeft;
33783                 this.anchor.focus();
33784                 noscroll.scrollLeft = l;
33785             }catch(e){}
33786         }
33787     },
33788
33789     toggleCheck : function(value){
33790         var cb = this.checkbox;
33791         if(cb){
33792             cb.checked = (value === undefined ? !cb.checked : value);
33793         }
33794     },
33795
33796     blur : function(){
33797         try{
33798             this.anchor.blur();
33799         }catch(e){}
33800     },
33801
33802     animExpand : function(callback){
33803         var ct = Roo.get(this.ctNode);
33804         ct.stopFx();
33805         if(!this.node.hasChildNodes()){
33806             this.updateExpandIcon();
33807             this.ctNode.style.display = "";
33808             Roo.callback(callback);
33809             return;
33810         }
33811         this.animating = true;
33812         this.updateExpandIcon();
33813
33814         ct.slideIn('t', {
33815            callback : function(){
33816                this.animating = false;
33817                Roo.callback(callback);
33818             },
33819             scope: this,
33820             duration: this.node.ownerTree.duration || .25
33821         });
33822     },
33823
33824     highlight : function(){
33825         var tree = this.node.getOwnerTree();
33826         Roo.fly(this.wrap).highlight(
33827             tree.hlColor || "C3DAF9",
33828             {endColor: tree.hlBaseColor}
33829         );
33830     },
33831
33832     collapse : function(){
33833         this.updateExpandIcon();
33834         this.ctNode.style.display = "none";
33835     },
33836
33837     animCollapse : function(callback){
33838         var ct = Roo.get(this.ctNode);
33839         ct.enableDisplayMode('block');
33840         ct.stopFx();
33841
33842         this.animating = true;
33843         this.updateExpandIcon();
33844
33845         ct.slideOut('t', {
33846             callback : function(){
33847                this.animating = false;
33848                Roo.callback(callback);
33849             },
33850             scope: this,
33851             duration: this.node.ownerTree.duration || .25
33852         });
33853     },
33854
33855     getContainer : function(){
33856         return this.ctNode;
33857     },
33858
33859     getEl : function(){
33860         return this.wrap;
33861     },
33862
33863     appendDDGhost : function(ghostNode){
33864         ghostNode.appendChild(this.elNode.cloneNode(true));
33865     },
33866
33867     getDDRepairXY : function(){
33868         return Roo.lib.Dom.getXY(this.iconNode);
33869     },
33870
33871     onRender : function(){
33872         this.render();
33873     },
33874
33875     render : function(bulkRender){
33876         var n = this.node, a = n.attributes;
33877         var targetNode = n.parentNode ?
33878               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33879
33880         if(!this.rendered){
33881             this.rendered = true;
33882
33883             this.renderElements(n, a, targetNode, bulkRender);
33884
33885             if(a.qtip){
33886                if(this.textNode.setAttributeNS){
33887                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33888                    if(a.qtipTitle){
33889                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33890                    }
33891                }else{
33892                    this.textNode.setAttribute("ext:qtip", a.qtip);
33893                    if(a.qtipTitle){
33894                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33895                    }
33896                }
33897             }else if(a.qtipCfg){
33898                 a.qtipCfg.target = Roo.id(this.textNode);
33899                 Roo.QuickTips.register(a.qtipCfg);
33900             }
33901             this.initEvents();
33902             if(!this.node.expanded){
33903                 this.updateExpandIcon();
33904             }
33905         }else{
33906             if(bulkRender === true) {
33907                 targetNode.appendChild(this.wrap);
33908             }
33909         }
33910     },
33911
33912     renderElements : function(n, a, targetNode, bulkRender)
33913     {
33914         // add some indent caching, this helps performance when rendering a large tree
33915         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33916         var t = n.getOwnerTree();
33917         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33918         if (typeof(n.attributes.html) != 'undefined') {
33919             txt = n.attributes.html;
33920         }
33921         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33922         var cb = typeof a.checked == 'boolean';
33923         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33924         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33925             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33926             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33927             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33928             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33929             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33930              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33931                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33932             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33933             "</li>"];
33934
33935         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33936             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33937                                 n.nextSibling.ui.getEl(), buf.join(""));
33938         }else{
33939             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33940         }
33941
33942         this.elNode = this.wrap.childNodes[0];
33943         this.ctNode = this.wrap.childNodes[1];
33944         var cs = this.elNode.childNodes;
33945         this.indentNode = cs[0];
33946         this.ecNode = cs[1];
33947         this.iconNode = cs[2];
33948         var index = 3;
33949         if(cb){
33950             this.checkbox = cs[3];
33951             index++;
33952         }
33953         this.anchor = cs[index];
33954         this.textNode = cs[index].firstChild;
33955     },
33956
33957     getAnchor : function(){
33958         return this.anchor;
33959     },
33960
33961     getTextEl : function(){
33962         return this.textNode;
33963     },
33964
33965     getIconEl : function(){
33966         return this.iconNode;
33967     },
33968
33969     isChecked : function(){
33970         return this.checkbox ? this.checkbox.checked : false;
33971     },
33972
33973     updateExpandIcon : function(){
33974         if(this.rendered){
33975             var n = this.node, c1, c2;
33976             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33977             var hasChild = n.hasChildNodes();
33978             if(hasChild){
33979                 if(n.expanded){
33980                     cls += "-minus";
33981                     c1 = "x-tree-node-collapsed";
33982                     c2 = "x-tree-node-expanded";
33983                 }else{
33984                     cls += "-plus";
33985                     c1 = "x-tree-node-expanded";
33986                     c2 = "x-tree-node-collapsed";
33987                 }
33988                 if(this.wasLeaf){
33989                     this.removeClass("x-tree-node-leaf");
33990                     this.wasLeaf = false;
33991                 }
33992                 if(this.c1 != c1 || this.c2 != c2){
33993                     Roo.fly(this.elNode).replaceClass(c1, c2);
33994                     this.c1 = c1; this.c2 = c2;
33995                 }
33996             }else{
33997                 // this changes non-leafs into leafs if they have no children.
33998                 // it's not very rational behaviour..
33999                 
34000                 if(!this.wasLeaf && this.node.leaf){
34001                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34002                     delete this.c1;
34003                     delete this.c2;
34004                     this.wasLeaf = true;
34005                 }
34006             }
34007             var ecc = "x-tree-ec-icon "+cls;
34008             if(this.ecc != ecc){
34009                 this.ecNode.className = ecc;
34010                 this.ecc = ecc;
34011             }
34012         }
34013     },
34014
34015     getChildIndent : function(){
34016         if(!this.childIndent){
34017             var buf = [];
34018             var p = this.node;
34019             while(p){
34020                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34021                     if(!p.isLast()) {
34022                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34023                     } else {
34024                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34025                     }
34026                 }
34027                 p = p.parentNode;
34028             }
34029             this.childIndent = buf.join("");
34030         }
34031         return this.childIndent;
34032     },
34033
34034     renderIndent : function(){
34035         if(this.rendered){
34036             var indent = "";
34037             var p = this.node.parentNode;
34038             if(p){
34039                 indent = p.ui.getChildIndent();
34040             }
34041             if(this.indentMarkup != indent){ // don't rerender if not required
34042                 this.indentNode.innerHTML = indent;
34043                 this.indentMarkup = indent;
34044             }
34045             this.updateExpandIcon();
34046         }
34047     }
34048 };
34049
34050 Roo.tree.RootTreeNodeUI = function(){
34051     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34052 };
34053 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34054     render : function(){
34055         if(!this.rendered){
34056             var targetNode = this.node.ownerTree.innerCt.dom;
34057             this.node.expanded = true;
34058             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34059             this.wrap = this.ctNode = targetNode.firstChild;
34060         }
34061     },
34062     collapse : function(){
34063     },
34064     expand : function(){
34065     }
34066 });/*
34067  * Based on:
34068  * Ext JS Library 1.1.1
34069  * Copyright(c) 2006-2007, Ext JS, LLC.
34070  *
34071  * Originally Released Under LGPL - original licence link has changed is not relivant.
34072  *
34073  * Fork - LGPL
34074  * <script type="text/javascript">
34075  */
34076 /**
34077  * @class Roo.tree.TreeLoader
34078  * @extends Roo.util.Observable
34079  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34080  * nodes from a specified URL. The response must be a javascript Array definition
34081  * who's elements are node definition objects. eg:
34082  * <pre><code>
34083 {  success : true,
34084    data :      [
34085    
34086     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34087     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34088     ]
34089 }
34090
34091
34092 </code></pre>
34093  * <br><br>
34094  * The old style respose with just an array is still supported, but not recommended.
34095  * <br><br>
34096  *
34097  * A server request is sent, and child nodes are loaded only when a node is expanded.
34098  * The loading node's id is passed to the server under the parameter name "node" to
34099  * enable the server to produce the correct child nodes.
34100  * <br><br>
34101  * To pass extra parameters, an event handler may be attached to the "beforeload"
34102  * event, and the parameters specified in the TreeLoader's baseParams property:
34103  * <pre><code>
34104     myTreeLoader.on("beforeload", function(treeLoader, node) {
34105         this.baseParams.category = node.attributes.category;
34106     }, this);
34107 </code></pre><
34108  * This would pass an HTTP parameter called "category" to the server containing
34109  * the value of the Node's "category" attribute.
34110  * @constructor
34111  * Creates a new Treeloader.
34112  * @param {Object} config A config object containing config properties.
34113  */
34114 Roo.tree.TreeLoader = function(config){
34115     this.baseParams = {};
34116     this.requestMethod = "POST";
34117     Roo.apply(this, config);
34118
34119     this.addEvents({
34120     
34121         /**
34122          * @event beforeload
34123          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34124          * @param {Object} This TreeLoader object.
34125          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34126          * @param {Object} callback The callback function specified in the {@link #load} call.
34127          */
34128         beforeload : true,
34129         /**
34130          * @event load
34131          * Fires when the node has been successfuly loaded.
34132          * @param {Object} This TreeLoader object.
34133          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34134          * @param {Object} response The response object containing the data from the server.
34135          */
34136         load : true,
34137         /**
34138          * @event loadexception
34139          * Fires if the network request failed.
34140          * @param {Object} This TreeLoader object.
34141          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34142          * @param {Object} response The response object containing the data from the server.
34143          */
34144         loadexception : true,
34145         /**
34146          * @event create
34147          * Fires before a node is created, enabling you to return custom Node types 
34148          * @param {Object} This TreeLoader object.
34149          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34150          */
34151         create : true
34152     });
34153
34154     Roo.tree.TreeLoader.superclass.constructor.call(this);
34155 };
34156
34157 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34158     /**
34159     * @cfg {String} dataUrl The URL from which to request a Json string which
34160     * specifies an array of node definition object representing the child nodes
34161     * to be loaded.
34162     */
34163     /**
34164     * @cfg {String} requestMethod either GET or POST
34165     * defaults to POST (due to BC)
34166     * to be loaded.
34167     */
34168     /**
34169     * @cfg {Object} baseParams (optional) An object containing properties which
34170     * specify HTTP parameters to be passed to each request for child nodes.
34171     */
34172     /**
34173     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34174     * created by this loader. If the attributes sent by the server have an attribute in this object,
34175     * they take priority.
34176     */
34177     /**
34178     * @cfg {Object} uiProviders (optional) An object containing properties which
34179     * 
34180     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34181     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34182     * <i>uiProvider</i> attribute of a returned child node is a string rather
34183     * than a reference to a TreeNodeUI implementation, this that string value
34184     * is used as a property name in the uiProviders object. You can define the provider named
34185     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34186     */
34187     uiProviders : {},
34188
34189     /**
34190     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34191     * child nodes before loading.
34192     */
34193     clearOnLoad : true,
34194
34195     /**
34196     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34197     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34198     * Grid query { data : [ .....] }
34199     */
34200     
34201     root : false,
34202      /**
34203     * @cfg {String} queryParam (optional) 
34204     * Name of the query as it will be passed on the querystring (defaults to 'node')
34205     * eg. the request will be ?node=[id]
34206     */
34207     
34208     
34209     queryParam: false,
34210     
34211     /**
34212      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34213      * This is called automatically when a node is expanded, but may be used to reload
34214      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34215      * @param {Roo.tree.TreeNode} node
34216      * @param {Function} callback
34217      */
34218     load : function(node, callback){
34219         if(this.clearOnLoad){
34220             while(node.firstChild){
34221                 node.removeChild(node.firstChild);
34222             }
34223         }
34224         if(node.attributes.children){ // preloaded json children
34225             var cs = node.attributes.children;
34226             for(var i = 0, len = cs.length; i < len; i++){
34227                 node.appendChild(this.createNode(cs[i]));
34228             }
34229             if(typeof callback == "function"){
34230                 callback();
34231             }
34232         }else if(this.dataUrl){
34233             this.requestData(node, callback);
34234         }
34235     },
34236
34237     getParams: function(node){
34238         var buf = [], bp = this.baseParams;
34239         for(var key in bp){
34240             if(typeof bp[key] != "function"){
34241                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34242             }
34243         }
34244         var n = this.queryParam === false ? 'node' : this.queryParam;
34245         buf.push(n + "=", encodeURIComponent(node.id));
34246         return buf.join("");
34247     },
34248
34249     requestData : function(node, callback){
34250         if(this.fireEvent("beforeload", this, node, callback) !== false){
34251             this.transId = Roo.Ajax.request({
34252                 method:this.requestMethod,
34253                 url: this.dataUrl||this.url,
34254                 success: this.handleResponse,
34255                 failure: this.handleFailure,
34256                 scope: this,
34257                 argument: {callback: callback, node: node},
34258                 params: this.getParams(node)
34259             });
34260         }else{
34261             // if the load is cancelled, make sure we notify
34262             // the node that we are done
34263             if(typeof callback == "function"){
34264                 callback();
34265             }
34266         }
34267     },
34268
34269     isLoading : function(){
34270         return this.transId ? true : false;
34271     },
34272
34273     abort : function(){
34274         if(this.isLoading()){
34275             Roo.Ajax.abort(this.transId);
34276         }
34277     },
34278
34279     // private
34280     createNode : function(attr)
34281     {
34282         // apply baseAttrs, nice idea Corey!
34283         if(this.baseAttrs){
34284             Roo.applyIf(attr, this.baseAttrs);
34285         }
34286         if(this.applyLoader !== false){
34287             attr.loader = this;
34288         }
34289         // uiProvider = depreciated..
34290         
34291         if(typeof(attr.uiProvider) == 'string'){
34292            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34293                 /**  eval:var:attr */ eval(attr.uiProvider);
34294         }
34295         if(typeof(this.uiProviders['default']) != 'undefined') {
34296             attr.uiProvider = this.uiProviders['default'];
34297         }
34298         
34299         this.fireEvent('create', this, attr);
34300         
34301         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34302         return(attr.leaf ?
34303                         new Roo.tree.TreeNode(attr) :
34304                         new Roo.tree.AsyncTreeNode(attr));
34305     },
34306
34307     processResponse : function(response, node, callback)
34308     {
34309         var json = response.responseText;
34310         try {
34311             
34312             var o = Roo.decode(json);
34313             
34314             if (this.root === false && typeof(o.success) != undefined) {
34315                 this.root = 'data'; // the default behaviour for list like data..
34316                 }
34317                 
34318             if (this.root !== false &&  !o.success) {
34319                 // it's a failure condition.
34320                 var a = response.argument;
34321                 this.fireEvent("loadexception", this, a.node, response);
34322                 Roo.log("Load failed - should have a handler really");
34323                 return;
34324             }
34325             
34326             
34327             
34328             if (this.root !== false) {
34329                  o = o[this.root];
34330             }
34331             
34332             for(var i = 0, len = o.length; i < len; i++){
34333                 var n = this.createNode(o[i]);
34334                 if(n){
34335                     node.appendChild(n);
34336                 }
34337             }
34338             if(typeof callback == "function"){
34339                 callback(this, node);
34340             }
34341         }catch(e){
34342             this.handleFailure(response);
34343         }
34344     },
34345
34346     handleResponse : function(response){
34347         this.transId = false;
34348         var a = response.argument;
34349         this.processResponse(response, a.node, a.callback);
34350         this.fireEvent("load", this, a.node, response);
34351     },
34352
34353     handleFailure : function(response)
34354     {
34355         // should handle failure better..
34356         this.transId = false;
34357         var a = response.argument;
34358         this.fireEvent("loadexception", this, a.node, response);
34359         if(typeof a.callback == "function"){
34360             a.callback(this, a.node);
34361         }
34362     }
34363 });/*
34364  * Based on:
34365  * Ext JS Library 1.1.1
34366  * Copyright(c) 2006-2007, Ext JS, LLC.
34367  *
34368  * Originally Released Under LGPL - original licence link has changed is not relivant.
34369  *
34370  * Fork - LGPL
34371  * <script type="text/javascript">
34372  */
34373
34374 /**
34375 * @class Roo.tree.TreeFilter
34376 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34377 * @param {TreePanel} tree
34378 * @param {Object} config (optional)
34379  */
34380 Roo.tree.TreeFilter = function(tree, config){
34381     this.tree = tree;
34382     this.filtered = {};
34383     Roo.apply(this, config);
34384 };
34385
34386 Roo.tree.TreeFilter.prototype = {
34387     clearBlank:false,
34388     reverse:false,
34389     autoClear:false,
34390     remove:false,
34391
34392      /**
34393      * Filter the data by a specific attribute.
34394      * @param {String/RegExp} value Either string that the attribute value
34395      * should start with or a RegExp to test against the attribute
34396      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34397      * @param {TreeNode} startNode (optional) The node to start the filter at.
34398      */
34399     filter : function(value, attr, startNode){
34400         attr = attr || "text";
34401         var f;
34402         if(typeof value == "string"){
34403             var vlen = value.length;
34404             // auto clear empty filter
34405             if(vlen == 0 && this.clearBlank){
34406                 this.clear();
34407                 return;
34408             }
34409             value = value.toLowerCase();
34410             f = function(n){
34411                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34412             };
34413         }else if(value.exec){ // regex?
34414             f = function(n){
34415                 return value.test(n.attributes[attr]);
34416             };
34417         }else{
34418             throw 'Illegal filter type, must be string or regex';
34419         }
34420         this.filterBy(f, null, startNode);
34421         },
34422
34423     /**
34424      * Filter by a function. The passed function will be called with each
34425      * node in the tree (or from the startNode). If the function returns true, the node is kept
34426      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34427      * @param {Function} fn The filter function
34428      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34429      */
34430     filterBy : function(fn, scope, startNode){
34431         startNode = startNode || this.tree.root;
34432         if(this.autoClear){
34433             this.clear();
34434         }
34435         var af = this.filtered, rv = this.reverse;
34436         var f = function(n){
34437             if(n == startNode){
34438                 return true;
34439             }
34440             if(af[n.id]){
34441                 return false;
34442             }
34443             var m = fn.call(scope || n, n);
34444             if(!m || rv){
34445                 af[n.id] = n;
34446                 n.ui.hide();
34447                 return false;
34448             }
34449             return true;
34450         };
34451         startNode.cascade(f);
34452         if(this.remove){
34453            for(var id in af){
34454                if(typeof id != "function"){
34455                    var n = af[id];
34456                    if(n && n.parentNode){
34457                        n.parentNode.removeChild(n);
34458                    }
34459                }
34460            }
34461         }
34462     },
34463
34464     /**
34465      * Clears the current filter. Note: with the "remove" option
34466      * set a filter cannot be cleared.
34467      */
34468     clear : function(){
34469         var t = this.tree;
34470         var af = this.filtered;
34471         for(var id in af){
34472             if(typeof id != "function"){
34473                 var n = af[id];
34474                 if(n){
34475                     n.ui.show();
34476                 }
34477             }
34478         }
34479         this.filtered = {};
34480     }
34481 };
34482 /*
34483  * Based on:
34484  * Ext JS Library 1.1.1
34485  * Copyright(c) 2006-2007, Ext JS, LLC.
34486  *
34487  * Originally Released Under LGPL - original licence link has changed is not relivant.
34488  *
34489  * Fork - LGPL
34490  * <script type="text/javascript">
34491  */
34492  
34493
34494 /**
34495  * @class Roo.tree.TreeSorter
34496  * Provides sorting of nodes in a TreePanel
34497  * 
34498  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34499  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34500  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34501  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34502  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34503  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34504  * @constructor
34505  * @param {TreePanel} tree
34506  * @param {Object} config
34507  */
34508 Roo.tree.TreeSorter = function(tree, config){
34509     Roo.apply(this, config);
34510     tree.on("beforechildrenrendered", this.doSort, this);
34511     tree.on("append", this.updateSort, this);
34512     tree.on("insert", this.updateSort, this);
34513     
34514     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34515     var p = this.property || "text";
34516     var sortType = this.sortType;
34517     var fs = this.folderSort;
34518     var cs = this.caseSensitive === true;
34519     var leafAttr = this.leafAttr || 'leaf';
34520
34521     this.sortFn = function(n1, n2){
34522         if(fs){
34523             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34524                 return 1;
34525             }
34526             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34527                 return -1;
34528             }
34529         }
34530         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34531         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34532         if(v1 < v2){
34533                         return dsc ? +1 : -1;
34534                 }else if(v1 > v2){
34535                         return dsc ? -1 : +1;
34536         }else{
34537                 return 0;
34538         }
34539     };
34540 };
34541
34542 Roo.tree.TreeSorter.prototype = {
34543     doSort : function(node){
34544         node.sort(this.sortFn);
34545     },
34546     
34547     compareNodes : function(n1, n2){
34548         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34549     },
34550     
34551     updateSort : function(tree, node){
34552         if(node.childrenRendered){
34553             this.doSort.defer(1, this, [node]);
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 if(Roo.dd.DropZone){
34568     
34569 Roo.tree.TreeDropZone = function(tree, config){
34570     this.allowParentInsert = false;
34571     this.allowContainerDrop = false;
34572     this.appendOnly = false;
34573     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34574     this.tree = tree;
34575     this.lastInsertClass = "x-tree-no-status";
34576     this.dragOverData = {};
34577 };
34578
34579 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34580     ddGroup : "TreeDD",
34581     scroll:  true,
34582     
34583     expandDelay : 1000,
34584     
34585     expandNode : function(node){
34586         if(node.hasChildNodes() && !node.isExpanded()){
34587             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34588         }
34589     },
34590     
34591     queueExpand : function(node){
34592         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34593     },
34594     
34595     cancelExpand : function(){
34596         if(this.expandProcId){
34597             clearTimeout(this.expandProcId);
34598             this.expandProcId = false;
34599         }
34600     },
34601     
34602     isValidDropPoint : function(n, pt, dd, e, data){
34603         if(!n || !data){ return false; }
34604         var targetNode = n.node;
34605         var dropNode = data.node;
34606         // default drop rules
34607         if(!(targetNode && targetNode.isTarget && pt)){
34608             return false;
34609         }
34610         if(pt == "append" && targetNode.allowChildren === false){
34611             return false;
34612         }
34613         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34614             return false;
34615         }
34616         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34617             return false;
34618         }
34619         // reuse the object
34620         var overEvent = this.dragOverData;
34621         overEvent.tree = this.tree;
34622         overEvent.target = targetNode;
34623         overEvent.data = data;
34624         overEvent.point = pt;
34625         overEvent.source = dd;
34626         overEvent.rawEvent = e;
34627         overEvent.dropNode = dropNode;
34628         overEvent.cancel = false;  
34629         var result = this.tree.fireEvent("nodedragover", overEvent);
34630         return overEvent.cancel === false && result !== false;
34631     },
34632     
34633     getDropPoint : function(e, n, dd)
34634     {
34635         var tn = n.node;
34636         if(tn.isRoot){
34637             return tn.allowChildren !== false ? "append" : false; // always append for root
34638         }
34639         var dragEl = n.ddel;
34640         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34641         var y = Roo.lib.Event.getPageY(e);
34642         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34643         
34644         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34645         var noAppend = tn.allowChildren === false;
34646         if(this.appendOnly || tn.parentNode.allowChildren === false){
34647             return noAppend ? false : "append";
34648         }
34649         var noBelow = false;
34650         if(!this.allowParentInsert){
34651             noBelow = tn.hasChildNodes() && tn.isExpanded();
34652         }
34653         var q = (b - t) / (noAppend ? 2 : 3);
34654         if(y >= t && y < (t + q)){
34655             return "above";
34656         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34657             return "below";
34658         }else{
34659             return "append";
34660         }
34661     },
34662     
34663     onNodeEnter : function(n, dd, e, data)
34664     {
34665         this.cancelExpand();
34666     },
34667     
34668     onNodeOver : function(n, dd, e, data)
34669     {
34670        
34671         var pt = this.getDropPoint(e, n, dd);
34672         var node = n.node;
34673         
34674         // auto node expand check
34675         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34676             this.queueExpand(node);
34677         }else if(pt != "append"){
34678             this.cancelExpand();
34679         }
34680         
34681         // set the insert point style on the target node
34682         var returnCls = this.dropNotAllowed;
34683         if(this.isValidDropPoint(n, pt, dd, e, data)){
34684            if(pt){
34685                var el = n.ddel;
34686                var cls;
34687                if(pt == "above"){
34688                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34689                    cls = "x-tree-drag-insert-above";
34690                }else if(pt == "below"){
34691                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34692                    cls = "x-tree-drag-insert-below";
34693                }else{
34694                    returnCls = "x-tree-drop-ok-append";
34695                    cls = "x-tree-drag-append";
34696                }
34697                if(this.lastInsertClass != cls){
34698                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34699                    this.lastInsertClass = cls;
34700                }
34701            }
34702        }
34703        return returnCls;
34704     },
34705     
34706     onNodeOut : function(n, dd, e, data){
34707         
34708         this.cancelExpand();
34709         this.removeDropIndicators(n);
34710     },
34711     
34712     onNodeDrop : function(n, dd, e, data){
34713         var point = this.getDropPoint(e, n, dd);
34714         var targetNode = n.node;
34715         targetNode.ui.startDrop();
34716         if(!this.isValidDropPoint(n, point, dd, e, data)){
34717             targetNode.ui.endDrop();
34718             return false;
34719         }
34720         // first try to find the drop node
34721         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34722         var dropEvent = {
34723             tree : this.tree,
34724             target: targetNode,
34725             data: data,
34726             point: point,
34727             source: dd,
34728             rawEvent: e,
34729             dropNode: dropNode,
34730             cancel: !dropNode   
34731         };
34732         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34733         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34734             targetNode.ui.endDrop();
34735             return false;
34736         }
34737         // allow target changing
34738         targetNode = dropEvent.target;
34739         if(point == "append" && !targetNode.isExpanded()){
34740             targetNode.expand(false, null, function(){
34741                 this.completeDrop(dropEvent);
34742             }.createDelegate(this));
34743         }else{
34744             this.completeDrop(dropEvent);
34745         }
34746         return true;
34747     },
34748     
34749     completeDrop : function(de){
34750         var ns = de.dropNode, p = de.point, t = de.target;
34751         if(!(ns instanceof Array)){
34752             ns = [ns];
34753         }
34754         var n;
34755         for(var i = 0, len = ns.length; i < len; i++){
34756             n = ns[i];
34757             if(p == "above"){
34758                 t.parentNode.insertBefore(n, t);
34759             }else if(p == "below"){
34760                 t.parentNode.insertBefore(n, t.nextSibling);
34761             }else{
34762                 t.appendChild(n);
34763             }
34764         }
34765         n.ui.focus();
34766         if(this.tree.hlDrop){
34767             n.ui.highlight();
34768         }
34769         t.ui.endDrop();
34770         this.tree.fireEvent("nodedrop", de);
34771     },
34772     
34773     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34774         if(this.tree.hlDrop){
34775             dropNode.ui.focus();
34776             dropNode.ui.highlight();
34777         }
34778         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34779     },
34780     
34781     getTree : function(){
34782         return this.tree;
34783     },
34784     
34785     removeDropIndicators : function(n){
34786         if(n && n.ddel){
34787             var el = n.ddel;
34788             Roo.fly(el).removeClass([
34789                     "x-tree-drag-insert-above",
34790                     "x-tree-drag-insert-below",
34791                     "x-tree-drag-append"]);
34792             this.lastInsertClass = "_noclass";
34793         }
34794     },
34795     
34796     beforeDragDrop : function(target, e, id){
34797         this.cancelExpand();
34798         return true;
34799     },
34800     
34801     afterRepair : function(data){
34802         if(data && Roo.enableFx){
34803             data.node.ui.highlight();
34804         }
34805         this.hideProxy();
34806     } 
34807     
34808 });
34809
34810 }
34811 /*
34812  * Based on:
34813  * Ext JS Library 1.1.1
34814  * Copyright(c) 2006-2007, Ext JS, LLC.
34815  *
34816  * Originally Released Under LGPL - original licence link has changed is not relivant.
34817  *
34818  * Fork - LGPL
34819  * <script type="text/javascript">
34820  */
34821  
34822
34823 if(Roo.dd.DragZone){
34824 Roo.tree.TreeDragZone = function(tree, config){
34825     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34826     this.tree = tree;
34827 };
34828
34829 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34830     ddGroup : "TreeDD",
34831    
34832     onBeforeDrag : function(data, e){
34833         var n = data.node;
34834         return n && n.draggable && !n.disabled;
34835     },
34836      
34837     
34838     onInitDrag : function(e){
34839         var data = this.dragData;
34840         this.tree.getSelectionModel().select(data.node);
34841         this.proxy.update("");
34842         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34843         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34844     },
34845     
34846     getRepairXY : function(e, data){
34847         return data.node.ui.getDDRepairXY();
34848     },
34849     
34850     onEndDrag : function(data, e){
34851         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34852         
34853         
34854     },
34855     
34856     onValidDrop : function(dd, e, id){
34857         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34858         this.hideProxy();
34859     },
34860     
34861     beforeInvalidDrop : function(e, id){
34862         // this scrolls the original position back into view
34863         var sm = this.tree.getSelectionModel();
34864         sm.clearSelections();
34865         sm.select(this.dragData.node);
34866     }
34867 });
34868 }/*
34869  * Based on:
34870  * Ext JS Library 1.1.1
34871  * Copyright(c) 2006-2007, Ext JS, LLC.
34872  *
34873  * Originally Released Under LGPL - original licence link has changed is not relivant.
34874  *
34875  * Fork - LGPL
34876  * <script type="text/javascript">
34877  */
34878 /**
34879  * @class Roo.tree.TreeEditor
34880  * @extends Roo.Editor
34881  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34882  * as the editor field.
34883  * @constructor
34884  * @param {Object} config (used to be the tree panel.)
34885  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34886  * 
34887  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34888  * @cfg {Roo.form.TextField|Object} field The field configuration
34889  *
34890  * 
34891  */
34892 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34893     var tree = config;
34894     var field;
34895     if (oldconfig) { // old style..
34896         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34897     } else {
34898         // new style..
34899         tree = config.tree;
34900         config.field = config.field  || {};
34901         config.field.xtype = 'TextField';
34902         field = Roo.factory(config.field, Roo.form);
34903     }
34904     config = config || {};
34905     
34906     
34907     this.addEvents({
34908         /**
34909          * @event beforenodeedit
34910          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34911          * false from the handler of this event.
34912          * @param {Editor} this
34913          * @param {Roo.tree.Node} node 
34914          */
34915         "beforenodeedit" : true
34916     });
34917     
34918     //Roo.log(config);
34919     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34920
34921     this.tree = tree;
34922
34923     tree.on('beforeclick', this.beforeNodeClick, this);
34924     tree.getTreeEl().on('mousedown', this.hide, this);
34925     this.on('complete', this.updateNode, this);
34926     this.on('beforestartedit', this.fitToTree, this);
34927     this.on('startedit', this.bindScroll, this, {delay:10});
34928     this.on('specialkey', this.onSpecialKey, this);
34929 };
34930
34931 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34932     /**
34933      * @cfg {String} alignment
34934      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34935      */
34936     alignment: "l-l",
34937     // inherit
34938     autoSize: false,
34939     /**
34940      * @cfg {Boolean} hideEl
34941      * True to hide the bound element while the editor is displayed (defaults to false)
34942      */
34943     hideEl : false,
34944     /**
34945      * @cfg {String} cls
34946      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34947      */
34948     cls: "x-small-editor x-tree-editor",
34949     /**
34950      * @cfg {Boolean} shim
34951      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34952      */
34953     shim:false,
34954     // inherit
34955     shadow:"frame",
34956     /**
34957      * @cfg {Number} maxWidth
34958      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34959      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34960      * scroll and client offsets into account prior to each edit.
34961      */
34962     maxWidth: 250,
34963
34964     editDelay : 350,
34965
34966     // private
34967     fitToTree : function(ed, el){
34968         var td = this.tree.getTreeEl().dom, nd = el.dom;
34969         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34970             td.scrollLeft = nd.offsetLeft;
34971         }
34972         var w = Math.min(
34973                 this.maxWidth,
34974                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34975         this.setSize(w, '');
34976         
34977         return this.fireEvent('beforenodeedit', this, this.editNode);
34978         
34979     },
34980
34981     // private
34982     triggerEdit : function(node){
34983         this.completeEdit();
34984         this.editNode = node;
34985         this.startEdit(node.ui.textNode, node.text);
34986     },
34987
34988     // private
34989     bindScroll : function(){
34990         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34991     },
34992
34993     // private
34994     beforeNodeClick : function(node, e){
34995         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34996         this.lastClick = new Date();
34997         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34998             e.stopEvent();
34999             this.triggerEdit(node);
35000             return false;
35001         }
35002         return true;
35003     },
35004
35005     // private
35006     updateNode : function(ed, value){
35007         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35008         this.editNode.setText(value);
35009     },
35010
35011     // private
35012     onHide : function(){
35013         Roo.tree.TreeEditor.superclass.onHide.call(this);
35014         if(this.editNode){
35015             this.editNode.ui.focus();
35016         }
35017     },
35018
35019     // private
35020     onSpecialKey : function(field, e){
35021         var k = e.getKey();
35022         if(k == e.ESC){
35023             e.stopEvent();
35024             this.cancelEdit();
35025         }else if(k == e.ENTER && !e.hasModifier()){
35026             e.stopEvent();
35027             this.completeEdit();
35028         }
35029     }
35030 });//<Script type="text/javascript">
35031 /*
35032  * Based on:
35033  * Ext JS Library 1.1.1
35034  * Copyright(c) 2006-2007, Ext JS, LLC.
35035  *
35036  * Originally Released Under LGPL - original licence link has changed is not relivant.
35037  *
35038  * Fork - LGPL
35039  * <script type="text/javascript">
35040  */
35041  
35042 /**
35043  * Not documented??? - probably should be...
35044  */
35045
35046 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35047     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35048     
35049     renderElements : function(n, a, targetNode, bulkRender){
35050         //consel.log("renderElements?");
35051         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35052
35053         var t = n.getOwnerTree();
35054         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35055         
35056         var cols = t.columns;
35057         var bw = t.borderWidth;
35058         var c = cols[0];
35059         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35060          var cb = typeof a.checked == "boolean";
35061         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35062         var colcls = 'x-t-' + tid + '-c0';
35063         var buf = [
35064             '<li class="x-tree-node">',
35065             
35066                 
35067                 '<div class="x-tree-node-el ', a.cls,'">',
35068                     // extran...
35069                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35070                 
35071                 
35072                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35073                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35074                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35075                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35076                            (a.iconCls ? ' '+a.iconCls : ''),
35077                            '" unselectable="on" />',
35078                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35079                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35080                              
35081                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35082                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35083                             '<span unselectable="on" qtip="' + tx + '">',
35084                              tx,
35085                              '</span></a>' ,
35086                     '</div>',
35087                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35088                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35089                  ];
35090         for(var i = 1, len = cols.length; i < len; i++){
35091             c = cols[i];
35092             colcls = 'x-t-' + tid + '-c' +i;
35093             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35094             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35095                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35096                       "</div>");
35097          }
35098          
35099          buf.push(
35100             '</a>',
35101             '<div class="x-clear"></div></div>',
35102             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35103             "</li>");
35104         
35105         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35106             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35107                                 n.nextSibling.ui.getEl(), buf.join(""));
35108         }else{
35109             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35110         }
35111         var el = this.wrap.firstChild;
35112         this.elRow = el;
35113         this.elNode = el.firstChild;
35114         this.ranchor = el.childNodes[1];
35115         this.ctNode = this.wrap.childNodes[1];
35116         var cs = el.firstChild.childNodes;
35117         this.indentNode = cs[0];
35118         this.ecNode = cs[1];
35119         this.iconNode = cs[2];
35120         var index = 3;
35121         if(cb){
35122             this.checkbox = cs[3];
35123             index++;
35124         }
35125         this.anchor = cs[index];
35126         
35127         this.textNode = cs[index].firstChild;
35128         
35129         //el.on("click", this.onClick, this);
35130         //el.on("dblclick", this.onDblClick, this);
35131         
35132         
35133        // console.log(this);
35134     },
35135     initEvents : function(){
35136         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35137         
35138             
35139         var a = this.ranchor;
35140
35141         var el = Roo.get(a);
35142
35143         if(Roo.isOpera){ // opera render bug ignores the CSS
35144             el.setStyle("text-decoration", "none");
35145         }
35146
35147         el.on("click", this.onClick, this);
35148         el.on("dblclick", this.onDblClick, this);
35149         el.on("contextmenu", this.onContextMenu, this);
35150         
35151     },
35152     
35153     /*onSelectedChange : function(state){
35154         if(state){
35155             this.focus();
35156             this.addClass("x-tree-selected");
35157         }else{
35158             //this.blur();
35159             this.removeClass("x-tree-selected");
35160         }
35161     },*/
35162     addClass : function(cls){
35163         if(this.elRow){
35164             Roo.fly(this.elRow).addClass(cls);
35165         }
35166         
35167     },
35168     
35169     
35170     removeClass : function(cls){
35171         if(this.elRow){
35172             Roo.fly(this.elRow).removeClass(cls);
35173         }
35174     }
35175
35176     
35177     
35178 });//<Script type="text/javascript">
35179
35180 /*
35181  * Based on:
35182  * Ext JS Library 1.1.1
35183  * Copyright(c) 2006-2007, Ext JS, LLC.
35184  *
35185  * Originally Released Under LGPL - original licence link has changed is not relivant.
35186  *
35187  * Fork - LGPL
35188  * <script type="text/javascript">
35189  */
35190  
35191
35192 /**
35193  * @class Roo.tree.ColumnTree
35194  * @extends Roo.data.TreePanel
35195  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35196  * @cfg {int} borderWidth  compined right/left border allowance
35197  * @constructor
35198  * @param {String/HTMLElement/Element} el The container element
35199  * @param {Object} config
35200  */
35201 Roo.tree.ColumnTree =  function(el, config)
35202 {
35203    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35204    this.addEvents({
35205         /**
35206         * @event resize
35207         * Fire this event on a container when it resizes
35208         * @param {int} w Width
35209         * @param {int} h Height
35210         */
35211        "resize" : true
35212     });
35213     this.on('resize', this.onResize, this);
35214 };
35215
35216 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35217     //lines:false,
35218     
35219     
35220     borderWidth: Roo.isBorderBox ? 0 : 2, 
35221     headEls : false,
35222     
35223     render : function(){
35224         // add the header.....
35225        
35226         Roo.tree.ColumnTree.superclass.render.apply(this);
35227         
35228         this.el.addClass('x-column-tree');
35229         
35230         this.headers = this.el.createChild(
35231             {cls:'x-tree-headers'},this.innerCt.dom);
35232    
35233         var cols = this.columns, c;
35234         var totalWidth = 0;
35235         this.headEls = [];
35236         var  len = cols.length;
35237         for(var i = 0; i < len; i++){
35238              c = cols[i];
35239              totalWidth += c.width;
35240             this.headEls.push(this.headers.createChild({
35241                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35242                  cn: {
35243                      cls:'x-tree-hd-text',
35244                      html: c.header
35245                  },
35246                  style:'width:'+(c.width-this.borderWidth)+'px;'
35247              }));
35248         }
35249         this.headers.createChild({cls:'x-clear'});
35250         // prevent floats from wrapping when clipped
35251         this.headers.setWidth(totalWidth);
35252         //this.innerCt.setWidth(totalWidth);
35253         this.innerCt.setStyle({ overflow: 'auto' });
35254         this.onResize(this.width, this.height);
35255              
35256         
35257     },
35258     onResize : function(w,h)
35259     {
35260         this.height = h;
35261         this.width = w;
35262         // resize cols..
35263         this.innerCt.setWidth(this.width);
35264         this.innerCt.setHeight(this.height-20);
35265         
35266         // headers...
35267         var cols = this.columns, c;
35268         var totalWidth = 0;
35269         var expEl = false;
35270         var len = cols.length;
35271         for(var i = 0; i < len; i++){
35272             c = cols[i];
35273             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35274                 // it's the expander..
35275                 expEl  = this.headEls[i];
35276                 continue;
35277             }
35278             totalWidth += c.width;
35279             
35280         }
35281         if (expEl) {
35282             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35283         }
35284         this.headers.setWidth(w-20);
35285
35286         
35287         
35288         
35289     }
35290 });
35291 /*
35292  * Based on:
35293  * Ext JS Library 1.1.1
35294  * Copyright(c) 2006-2007, Ext JS, LLC.
35295  *
35296  * Originally Released Under LGPL - original licence link has changed is not relivant.
35297  *
35298  * Fork - LGPL
35299  * <script type="text/javascript">
35300  */
35301  
35302 /**
35303  * @class Roo.menu.Menu
35304  * @extends Roo.util.Observable
35305  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35306  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35307  * @constructor
35308  * Creates a new Menu
35309  * @param {Object} config Configuration options
35310  */
35311 Roo.menu.Menu = function(config){
35312     Roo.apply(this, config);
35313     this.id = this.id || Roo.id();
35314     this.addEvents({
35315         /**
35316          * @event beforeshow
35317          * Fires before this menu is displayed
35318          * @param {Roo.menu.Menu} this
35319          */
35320         beforeshow : true,
35321         /**
35322          * @event beforehide
35323          * Fires before this menu is hidden
35324          * @param {Roo.menu.Menu} this
35325          */
35326         beforehide : true,
35327         /**
35328          * @event show
35329          * Fires after this menu is displayed
35330          * @param {Roo.menu.Menu} this
35331          */
35332         show : true,
35333         /**
35334          * @event hide
35335          * Fires after this menu is hidden
35336          * @param {Roo.menu.Menu} this
35337          */
35338         hide : true,
35339         /**
35340          * @event click
35341          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35342          * @param {Roo.menu.Menu} this
35343          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35344          * @param {Roo.EventObject} e
35345          */
35346         click : true,
35347         /**
35348          * @event mouseover
35349          * Fires when the mouse is hovering over this menu
35350          * @param {Roo.menu.Menu} this
35351          * @param {Roo.EventObject} e
35352          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35353          */
35354         mouseover : true,
35355         /**
35356          * @event mouseout
35357          * Fires when the mouse exits this menu
35358          * @param {Roo.menu.Menu} this
35359          * @param {Roo.EventObject} e
35360          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35361          */
35362         mouseout : true,
35363         /**
35364          * @event itemclick
35365          * Fires when a menu item contained in this menu is clicked
35366          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35367          * @param {Roo.EventObject} e
35368          */
35369         itemclick: true
35370     });
35371     if (this.registerMenu) {
35372         Roo.menu.MenuMgr.register(this);
35373     }
35374     
35375     var mis = this.items;
35376     this.items = new Roo.util.MixedCollection();
35377     if(mis){
35378         this.add.apply(this, mis);
35379     }
35380 };
35381
35382 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35383     /**
35384      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35385      */
35386     minWidth : 120,
35387     /**
35388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35389      * for bottom-right shadow (defaults to "sides")
35390      */
35391     shadow : "sides",
35392     /**
35393      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35394      * this menu (defaults to "tl-tr?")
35395      */
35396     subMenuAlign : "tl-tr?",
35397     /**
35398      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35399      * relative to its element of origin (defaults to "tl-bl?")
35400      */
35401     defaultAlign : "tl-bl?",
35402     /**
35403      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35404      */
35405     allowOtherMenus : false,
35406     /**
35407      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35408      */
35409     registerMenu : true,
35410
35411     hidden:true,
35412
35413     // private
35414     render : function(){
35415         if(this.el){
35416             return;
35417         }
35418         var el = this.el = new Roo.Layer({
35419             cls: "x-menu",
35420             shadow:this.shadow,
35421             constrain: false,
35422             parentEl: this.parentEl || document.body,
35423             zindex:15000
35424         });
35425
35426         this.keyNav = new Roo.menu.MenuNav(this);
35427
35428         if(this.plain){
35429             el.addClass("x-menu-plain");
35430         }
35431         if(this.cls){
35432             el.addClass(this.cls);
35433         }
35434         // generic focus element
35435         this.focusEl = el.createChild({
35436             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35437         });
35438         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35439         ul.on("click", this.onClick, this);
35440         ul.on("mouseover", this.onMouseOver, this);
35441         ul.on("mouseout", this.onMouseOut, this);
35442         this.items.each(function(item){
35443             if (item.hidden) {
35444                 return;
35445             }
35446             
35447             var li = document.createElement("li");
35448             li.className = "x-menu-list-item";
35449             ul.dom.appendChild(li);
35450             item.render(li, this);
35451         }, this);
35452         this.ul = ul;
35453         this.autoWidth();
35454     },
35455
35456     // private
35457     autoWidth : function(){
35458         var el = this.el, ul = this.ul;
35459         if(!el){
35460             return;
35461         }
35462         var w = this.width;
35463         if(w){
35464             el.setWidth(w);
35465         }else if(Roo.isIE){
35466             el.setWidth(this.minWidth);
35467             var t = el.dom.offsetWidth; // force recalc
35468             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35469         }
35470     },
35471
35472     // private
35473     delayAutoWidth : function(){
35474         if(this.rendered){
35475             if(!this.awTask){
35476                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35477             }
35478             this.awTask.delay(20);
35479         }
35480     },
35481
35482     // private
35483     findTargetItem : function(e){
35484         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35485         if(t && t.menuItemId){
35486             return this.items.get(t.menuItemId);
35487         }
35488     },
35489
35490     // private
35491     onClick : function(e){
35492         var t;
35493         if(t = this.findTargetItem(e)){
35494             t.onClick(e);
35495             this.fireEvent("click", this, t, e);
35496         }
35497     },
35498
35499     // private
35500     setActiveItem : function(item, autoExpand){
35501         if(item != this.activeItem){
35502             if(this.activeItem){
35503                 this.activeItem.deactivate();
35504             }
35505             this.activeItem = item;
35506             item.activate(autoExpand);
35507         }else if(autoExpand){
35508             item.expandMenu();
35509         }
35510     },
35511
35512     // private
35513     tryActivate : function(start, step){
35514         var items = this.items;
35515         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35516             var item = items.get(i);
35517             if(!item.disabled && item.canActivate){
35518                 this.setActiveItem(item, false);
35519                 return item;
35520             }
35521         }
35522         return false;
35523     },
35524
35525     // private
35526     onMouseOver : function(e){
35527         var t;
35528         if(t = this.findTargetItem(e)){
35529             if(t.canActivate && !t.disabled){
35530                 this.setActiveItem(t, true);
35531             }
35532         }
35533         this.fireEvent("mouseover", this, e, t);
35534     },
35535
35536     // private
35537     onMouseOut : function(e){
35538         var t;
35539         if(t = this.findTargetItem(e)){
35540             if(t == this.activeItem && t.shouldDeactivate(e)){
35541                 this.activeItem.deactivate();
35542                 delete this.activeItem;
35543             }
35544         }
35545         this.fireEvent("mouseout", this, e, t);
35546     },
35547
35548     /**
35549      * Read-only.  Returns true if the menu is currently displayed, else false.
35550      * @type Boolean
35551      */
35552     isVisible : function(){
35553         return this.el && !this.hidden;
35554     },
35555
35556     /**
35557      * Displays this menu relative to another element
35558      * @param {String/HTMLElement/Roo.Element} element The element to align to
35559      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35560      * the element (defaults to this.defaultAlign)
35561      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35562      */
35563     show : function(el, pos, parentMenu){
35564         this.parentMenu = parentMenu;
35565         if(!this.el){
35566             this.render();
35567         }
35568         this.fireEvent("beforeshow", this);
35569         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35570     },
35571
35572     /**
35573      * Displays this menu at a specific xy position
35574      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35575      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35576      */
35577     showAt : function(xy, parentMenu, /* private: */_e){
35578         this.parentMenu = parentMenu;
35579         if(!this.el){
35580             this.render();
35581         }
35582         if(_e !== false){
35583             this.fireEvent("beforeshow", this);
35584             xy = this.el.adjustForConstraints(xy);
35585         }
35586         this.el.setXY(xy);
35587         this.el.show();
35588         this.hidden = false;
35589         this.focus();
35590         this.fireEvent("show", this);
35591     },
35592
35593     focus : function(){
35594         if(!this.hidden){
35595             this.doFocus.defer(50, this);
35596         }
35597     },
35598
35599     doFocus : function(){
35600         if(!this.hidden){
35601             this.focusEl.focus();
35602         }
35603     },
35604
35605     /**
35606      * Hides this menu and optionally all parent menus
35607      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35608      */
35609     hide : function(deep){
35610         if(this.el && this.isVisible()){
35611             this.fireEvent("beforehide", this);
35612             if(this.activeItem){
35613                 this.activeItem.deactivate();
35614                 this.activeItem = null;
35615             }
35616             this.el.hide();
35617             this.hidden = true;
35618             this.fireEvent("hide", this);
35619         }
35620         if(deep === true && this.parentMenu){
35621             this.parentMenu.hide(true);
35622         }
35623     },
35624
35625     /**
35626      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35627      * Any of the following are valid:
35628      * <ul>
35629      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35630      * <li>An HTMLElement object which will be converted to a menu item</li>
35631      * <li>A menu item config object that will be created as a new menu item</li>
35632      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35633      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35634      * </ul>
35635      * Usage:
35636      * <pre><code>
35637 // Create the menu
35638 var menu = new Roo.menu.Menu();
35639
35640 // Create a menu item to add by reference
35641 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35642
35643 // Add a bunch of items at once using different methods.
35644 // Only the last item added will be returned.
35645 var item = menu.add(
35646     menuItem,                // add existing item by ref
35647     'Dynamic Item',          // new TextItem
35648     '-',                     // new separator
35649     { text: 'Config Item' }  // new item by config
35650 );
35651 </code></pre>
35652      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35653      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35654      */
35655     add : function(){
35656         var a = arguments, l = a.length, item;
35657         for(var i = 0; i < l; i++){
35658             var el = a[i];
35659             if ((typeof(el) == "object") && el.xtype && el.xns) {
35660                 el = Roo.factory(el, Roo.menu);
35661             }
35662             
35663             if(el.render){ // some kind of Item
35664                 item = this.addItem(el);
35665             }else if(typeof el == "string"){ // string
35666                 if(el == "separator" || el == "-"){
35667                     item = this.addSeparator();
35668                 }else{
35669                     item = this.addText(el);
35670                 }
35671             }else if(el.tagName || el.el){ // element
35672                 item = this.addElement(el);
35673             }else if(typeof el == "object"){ // must be menu item config?
35674                 item = this.addMenuItem(el);
35675             }
35676         }
35677         return item;
35678     },
35679
35680     /**
35681      * Returns this menu's underlying {@link Roo.Element} object
35682      * @return {Roo.Element} The element
35683      */
35684     getEl : function(){
35685         if(!this.el){
35686             this.render();
35687         }
35688         return this.el;
35689     },
35690
35691     /**
35692      * Adds a separator bar to the menu
35693      * @return {Roo.menu.Item} The menu item that was added
35694      */
35695     addSeparator : function(){
35696         return this.addItem(new Roo.menu.Separator());
35697     },
35698
35699     /**
35700      * Adds an {@link Roo.Element} object to the menu
35701      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35702      * @return {Roo.menu.Item} The menu item that was added
35703      */
35704     addElement : function(el){
35705         return this.addItem(new Roo.menu.BaseItem(el));
35706     },
35707
35708     /**
35709      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35710      * @param {Roo.menu.Item} item The menu item to add
35711      * @return {Roo.menu.Item} The menu item that was added
35712      */
35713     addItem : function(item){
35714         this.items.add(item);
35715         if(this.ul){
35716             var li = document.createElement("li");
35717             li.className = "x-menu-list-item";
35718             this.ul.dom.appendChild(li);
35719             item.render(li, this);
35720             this.delayAutoWidth();
35721         }
35722         return item;
35723     },
35724
35725     /**
35726      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35727      * @param {Object} config A MenuItem config object
35728      * @return {Roo.menu.Item} The menu item that was added
35729      */
35730     addMenuItem : function(config){
35731         if(!(config instanceof Roo.menu.Item)){
35732             if(typeof config.checked == "boolean"){ // must be check menu item config?
35733                 config = new Roo.menu.CheckItem(config);
35734             }else{
35735                 config = new Roo.menu.Item(config);
35736             }
35737         }
35738         return this.addItem(config);
35739     },
35740
35741     /**
35742      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35743      * @param {String} text The text to display in the menu item
35744      * @return {Roo.menu.Item} The menu item that was added
35745      */
35746     addText : function(text){
35747         return this.addItem(new Roo.menu.TextItem({ text : text }));
35748     },
35749
35750     /**
35751      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35752      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35753      * @param {Roo.menu.Item} item The menu item to add
35754      * @return {Roo.menu.Item} The menu item that was added
35755      */
35756     insert : function(index, item){
35757         this.items.insert(index, item);
35758         if(this.ul){
35759             var li = document.createElement("li");
35760             li.className = "x-menu-list-item";
35761             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35762             item.render(li, this);
35763             this.delayAutoWidth();
35764         }
35765         return item;
35766     },
35767
35768     /**
35769      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35770      * @param {Roo.menu.Item} item The menu item to remove
35771      */
35772     remove : function(item){
35773         this.items.removeKey(item.id);
35774         item.destroy();
35775     },
35776
35777     /**
35778      * Removes and destroys all items in the menu
35779      */
35780     removeAll : function(){
35781         var f;
35782         while(f = this.items.first()){
35783             this.remove(f);
35784         }
35785     }
35786 });
35787
35788 // MenuNav is a private utility class used internally by the Menu
35789 Roo.menu.MenuNav = function(menu){
35790     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35791     this.scope = this.menu = menu;
35792 };
35793
35794 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35795     doRelay : function(e, h){
35796         var k = e.getKey();
35797         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35798             this.menu.tryActivate(0, 1);
35799             return false;
35800         }
35801         return h.call(this.scope || this, e, this.menu);
35802     },
35803
35804     up : function(e, m){
35805         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35806             m.tryActivate(m.items.length-1, -1);
35807         }
35808     },
35809
35810     down : function(e, m){
35811         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35812             m.tryActivate(0, 1);
35813         }
35814     },
35815
35816     right : function(e, m){
35817         if(m.activeItem){
35818             m.activeItem.expandMenu(true);
35819         }
35820     },
35821
35822     left : function(e, m){
35823         m.hide();
35824         if(m.parentMenu && m.parentMenu.activeItem){
35825             m.parentMenu.activeItem.activate();
35826         }
35827     },
35828
35829     enter : function(e, m){
35830         if(m.activeItem){
35831             e.stopPropagation();
35832             m.activeItem.onClick(e);
35833             m.fireEvent("click", this, m.activeItem);
35834             return true;
35835         }
35836     }
35837 });/*
35838  * Based on:
35839  * Ext JS Library 1.1.1
35840  * Copyright(c) 2006-2007, Ext JS, LLC.
35841  *
35842  * Originally Released Under LGPL - original licence link has changed is not relivant.
35843  *
35844  * Fork - LGPL
35845  * <script type="text/javascript">
35846  */
35847  
35848 /**
35849  * @class Roo.menu.MenuMgr
35850  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35851  * @singleton
35852  */
35853 Roo.menu.MenuMgr = function(){
35854    var menus, active, groups = {}, attached = false, lastShow = new Date();
35855
35856    // private - called when first menu is created
35857    function init(){
35858        menus = {};
35859        active = new Roo.util.MixedCollection();
35860        Roo.get(document).addKeyListener(27, function(){
35861            if(active.length > 0){
35862                hideAll();
35863            }
35864        });
35865    }
35866
35867    // private
35868    function hideAll(){
35869        if(active && active.length > 0){
35870            var c = active.clone();
35871            c.each(function(m){
35872                m.hide();
35873            });
35874        }
35875    }
35876
35877    // private
35878    function onHide(m){
35879        active.remove(m);
35880        if(active.length < 1){
35881            Roo.get(document).un("mousedown", onMouseDown);
35882            attached = false;
35883        }
35884    }
35885
35886    // private
35887    function onShow(m){
35888        var last = active.last();
35889        lastShow = new Date();
35890        active.add(m);
35891        if(!attached){
35892            Roo.get(document).on("mousedown", onMouseDown);
35893            attached = true;
35894        }
35895        if(m.parentMenu){
35896           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35897           m.parentMenu.activeChild = m;
35898        }else if(last && last.isVisible()){
35899           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35900        }
35901    }
35902
35903    // private
35904    function onBeforeHide(m){
35905        if(m.activeChild){
35906            m.activeChild.hide();
35907        }
35908        if(m.autoHideTimer){
35909            clearTimeout(m.autoHideTimer);
35910            delete m.autoHideTimer;
35911        }
35912    }
35913
35914    // private
35915    function onBeforeShow(m){
35916        var pm = m.parentMenu;
35917        if(!pm && !m.allowOtherMenus){
35918            hideAll();
35919        }else if(pm && pm.activeChild && active != m){
35920            pm.activeChild.hide();
35921        }
35922    }
35923
35924    // private
35925    function onMouseDown(e){
35926        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35927            hideAll();
35928        }
35929    }
35930
35931    // private
35932    function onBeforeCheck(mi, state){
35933        if(state){
35934            var g = groups[mi.group];
35935            for(var i = 0, l = g.length; i < l; i++){
35936                if(g[i] != mi){
35937                    g[i].setChecked(false);
35938                }
35939            }
35940        }
35941    }
35942
35943    return {
35944
35945        /**
35946         * Hides all menus that are currently visible
35947         */
35948        hideAll : function(){
35949             hideAll();  
35950        },
35951
35952        // private
35953        register : function(menu){
35954            if(!menus){
35955                init();
35956            }
35957            menus[menu.id] = menu;
35958            menu.on("beforehide", onBeforeHide);
35959            menu.on("hide", onHide);
35960            menu.on("beforeshow", onBeforeShow);
35961            menu.on("show", onShow);
35962            var g = menu.group;
35963            if(g && menu.events["checkchange"]){
35964                if(!groups[g]){
35965                    groups[g] = [];
35966                }
35967                groups[g].push(menu);
35968                menu.on("checkchange", onCheck);
35969            }
35970        },
35971
35972         /**
35973          * Returns a {@link Roo.menu.Menu} object
35974          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35975          * be used to generate and return a new Menu instance.
35976          */
35977        get : function(menu){
35978            if(typeof menu == "string"){ // menu id
35979                return menus[menu];
35980            }else if(menu.events){  // menu instance
35981                return menu;
35982            }else if(typeof menu.length == 'number'){ // array of menu items?
35983                return new Roo.menu.Menu({items:menu});
35984            }else{ // otherwise, must be a config
35985                return new Roo.menu.Menu(menu);
35986            }
35987        },
35988
35989        // private
35990        unregister : function(menu){
35991            delete menus[menu.id];
35992            menu.un("beforehide", onBeforeHide);
35993            menu.un("hide", onHide);
35994            menu.un("beforeshow", onBeforeShow);
35995            menu.un("show", onShow);
35996            var g = menu.group;
35997            if(g && menu.events["checkchange"]){
35998                groups[g].remove(menu);
35999                menu.un("checkchange", onCheck);
36000            }
36001        },
36002
36003        // private
36004        registerCheckable : function(menuItem){
36005            var g = menuItem.group;
36006            if(g){
36007                if(!groups[g]){
36008                    groups[g] = [];
36009                }
36010                groups[g].push(menuItem);
36011                menuItem.on("beforecheckchange", onBeforeCheck);
36012            }
36013        },
36014
36015        // private
36016        unregisterCheckable : function(menuItem){
36017            var g = menuItem.group;
36018            if(g){
36019                groups[g].remove(menuItem);
36020                menuItem.un("beforecheckchange", onBeforeCheck);
36021            }
36022        }
36023    };
36024 }();/*
36025  * Based on:
36026  * Ext JS Library 1.1.1
36027  * Copyright(c) 2006-2007, Ext JS, LLC.
36028  *
36029  * Originally Released Under LGPL - original licence link has changed is not relivant.
36030  *
36031  * Fork - LGPL
36032  * <script type="text/javascript">
36033  */
36034  
36035
36036 /**
36037  * @class Roo.menu.BaseItem
36038  * @extends Roo.Component
36039  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36040  * management and base configuration options shared by all menu components.
36041  * @constructor
36042  * Creates a new BaseItem
36043  * @param {Object} config Configuration options
36044  */
36045 Roo.menu.BaseItem = function(config){
36046     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36047
36048     this.addEvents({
36049         /**
36050          * @event click
36051          * Fires when this item is clicked
36052          * @param {Roo.menu.BaseItem} this
36053          * @param {Roo.EventObject} e
36054          */
36055         click: true,
36056         /**
36057          * @event activate
36058          * Fires when this item is activated
36059          * @param {Roo.menu.BaseItem} this
36060          */
36061         activate : true,
36062         /**
36063          * @event deactivate
36064          * Fires when this item is deactivated
36065          * @param {Roo.menu.BaseItem} this
36066          */
36067         deactivate : true
36068     });
36069
36070     if(this.handler){
36071         this.on("click", this.handler, this.scope, true);
36072     }
36073 };
36074
36075 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36076     /**
36077      * @cfg {Function} handler
36078      * A function that will handle the click event of this menu item (defaults to undefined)
36079      */
36080     /**
36081      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36082      */
36083     canActivate : false,
36084     
36085      /**
36086      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36087      */
36088     hidden: false,
36089     
36090     /**
36091      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36092      */
36093     activeClass : "x-menu-item-active",
36094     /**
36095      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36096      */
36097     hideOnClick : true,
36098     /**
36099      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36100      */
36101     hideDelay : 100,
36102
36103     // private
36104     ctype: "Roo.menu.BaseItem",
36105
36106     // private
36107     actionMode : "container",
36108
36109     // private
36110     render : function(container, parentMenu){
36111         this.parentMenu = parentMenu;
36112         Roo.menu.BaseItem.superclass.render.call(this, container);
36113         this.container.menuItemId = this.id;
36114     },
36115
36116     // private
36117     onRender : function(container, position){
36118         this.el = Roo.get(this.el);
36119         container.dom.appendChild(this.el.dom);
36120     },
36121
36122     // private
36123     onClick : function(e){
36124         if(!this.disabled && this.fireEvent("click", this, e) !== false
36125                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36126             this.handleClick(e);
36127         }else{
36128             e.stopEvent();
36129         }
36130     },
36131
36132     // private
36133     activate : function(){
36134         if(this.disabled){
36135             return false;
36136         }
36137         var li = this.container;
36138         li.addClass(this.activeClass);
36139         this.region = li.getRegion().adjust(2, 2, -2, -2);
36140         this.fireEvent("activate", this);
36141         return true;
36142     },
36143
36144     // private
36145     deactivate : function(){
36146         this.container.removeClass(this.activeClass);
36147         this.fireEvent("deactivate", this);
36148     },
36149
36150     // private
36151     shouldDeactivate : function(e){
36152         return !this.region || !this.region.contains(e.getPoint());
36153     },
36154
36155     // private
36156     handleClick : function(e){
36157         if(this.hideOnClick){
36158             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36159         }
36160     },
36161
36162     // private
36163     expandMenu : function(autoActivate){
36164         // do nothing
36165     },
36166
36167     // private
36168     hideMenu : function(){
36169         // do nothing
36170     }
36171 });/*
36172  * Based on:
36173  * Ext JS Library 1.1.1
36174  * Copyright(c) 2006-2007, Ext JS, LLC.
36175  *
36176  * Originally Released Under LGPL - original licence link has changed is not relivant.
36177  *
36178  * Fork - LGPL
36179  * <script type="text/javascript">
36180  */
36181  
36182 /**
36183  * @class Roo.menu.Adapter
36184  * @extends Roo.menu.BaseItem
36185  * 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.
36186  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36187  * @constructor
36188  * Creates a new Adapter
36189  * @param {Object} config Configuration options
36190  */
36191 Roo.menu.Adapter = function(component, config){
36192     Roo.menu.Adapter.superclass.constructor.call(this, config);
36193     this.component = component;
36194 };
36195 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36196     // private
36197     canActivate : true,
36198
36199     // private
36200     onRender : function(container, position){
36201         this.component.render(container);
36202         this.el = this.component.getEl();
36203     },
36204
36205     // private
36206     activate : function(){
36207         if(this.disabled){
36208             return false;
36209         }
36210         this.component.focus();
36211         this.fireEvent("activate", this);
36212         return true;
36213     },
36214
36215     // private
36216     deactivate : function(){
36217         this.fireEvent("deactivate", this);
36218     },
36219
36220     // private
36221     disable : function(){
36222         this.component.disable();
36223         Roo.menu.Adapter.superclass.disable.call(this);
36224     },
36225
36226     // private
36227     enable : function(){
36228         this.component.enable();
36229         Roo.menu.Adapter.superclass.enable.call(this);
36230     }
36231 });/*
36232  * Based on:
36233  * Ext JS Library 1.1.1
36234  * Copyright(c) 2006-2007, Ext JS, LLC.
36235  *
36236  * Originally Released Under LGPL - original licence link has changed is not relivant.
36237  *
36238  * Fork - LGPL
36239  * <script type="text/javascript">
36240  */
36241
36242 /**
36243  * @class Roo.menu.TextItem
36244  * @extends Roo.menu.BaseItem
36245  * Adds a static text string to a menu, usually used as either a heading or group separator.
36246  * Note: old style constructor with text is still supported.
36247  * 
36248  * @constructor
36249  * Creates a new TextItem
36250  * @param {Object} cfg Configuration
36251  */
36252 Roo.menu.TextItem = function(cfg){
36253     if (typeof(cfg) == 'string') {
36254         this.text = cfg;
36255     } else {
36256         Roo.apply(this,cfg);
36257     }
36258     
36259     Roo.menu.TextItem.superclass.constructor.call(this);
36260 };
36261
36262 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36263     /**
36264      * @cfg {Boolean} text Text to show on item.
36265      */
36266     text : '',
36267     
36268     /**
36269      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36270      */
36271     hideOnClick : false,
36272     /**
36273      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36274      */
36275     itemCls : "x-menu-text",
36276
36277     // private
36278     onRender : function(){
36279         var s = document.createElement("span");
36280         s.className = this.itemCls;
36281         s.innerHTML = this.text;
36282         this.el = s;
36283         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36284     }
36285 });/*
36286  * Based on:
36287  * Ext JS Library 1.1.1
36288  * Copyright(c) 2006-2007, Ext JS, LLC.
36289  *
36290  * Originally Released Under LGPL - original licence link has changed is not relivant.
36291  *
36292  * Fork - LGPL
36293  * <script type="text/javascript">
36294  */
36295
36296 /**
36297  * @class Roo.menu.Separator
36298  * @extends Roo.menu.BaseItem
36299  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36300  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36301  * @constructor
36302  * @param {Object} config Configuration options
36303  */
36304 Roo.menu.Separator = function(config){
36305     Roo.menu.Separator.superclass.constructor.call(this, config);
36306 };
36307
36308 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36309     /**
36310      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36311      */
36312     itemCls : "x-menu-sep",
36313     /**
36314      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36315      */
36316     hideOnClick : false,
36317
36318     // private
36319     onRender : function(li){
36320         var s = document.createElement("span");
36321         s.className = this.itemCls;
36322         s.innerHTML = "&#160;";
36323         this.el = s;
36324         li.addClass("x-menu-sep-li");
36325         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36326     }
36327 });/*
36328  * Based on:
36329  * Ext JS Library 1.1.1
36330  * Copyright(c) 2006-2007, Ext JS, LLC.
36331  *
36332  * Originally Released Under LGPL - original licence link has changed is not relivant.
36333  *
36334  * Fork - LGPL
36335  * <script type="text/javascript">
36336  */
36337 /**
36338  * @class Roo.menu.Item
36339  * @extends Roo.menu.BaseItem
36340  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36341  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36342  * activation and click handling.
36343  * @constructor
36344  * Creates a new Item
36345  * @param {Object} config Configuration options
36346  */
36347 Roo.menu.Item = function(config){
36348     Roo.menu.Item.superclass.constructor.call(this, config);
36349     if(this.menu){
36350         this.menu = Roo.menu.MenuMgr.get(this.menu);
36351     }
36352 };
36353 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36354     
36355     /**
36356      * @cfg {String} text
36357      * The text to show on the menu item.
36358      */
36359     text: '',
36360      /**
36361      * @cfg {String} HTML to render in menu
36362      * The text to show on the menu item (HTML version).
36363      */
36364     html: '',
36365     /**
36366      * @cfg {String} icon
36367      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36368      */
36369     icon: undefined,
36370     /**
36371      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36372      */
36373     itemCls : "x-menu-item",
36374     /**
36375      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36376      */
36377     canActivate : true,
36378     /**
36379      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36380      */
36381     showDelay: 200,
36382     // doc'd in BaseItem
36383     hideDelay: 200,
36384
36385     // private
36386     ctype: "Roo.menu.Item",
36387     
36388     // private
36389     onRender : function(container, position){
36390         var el = document.createElement("a");
36391         el.hideFocus = true;
36392         el.unselectable = "on";
36393         el.href = this.href || "#";
36394         if(this.hrefTarget){
36395             el.target = this.hrefTarget;
36396         }
36397         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36398         
36399         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36400         
36401         el.innerHTML = String.format(
36402                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36403                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36404         this.el = el;
36405         Roo.menu.Item.superclass.onRender.call(this, container, position);
36406     },
36407
36408     /**
36409      * Sets the text to display in this menu item
36410      * @param {String} text The text to display
36411      * @param {Boolean} isHTML true to indicate text is pure html.
36412      */
36413     setText : function(text, isHTML){
36414         if (isHTML) {
36415             this.html = text;
36416         } else {
36417             this.text = text;
36418             this.html = '';
36419         }
36420         if(this.rendered){
36421             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36422      
36423             this.el.update(String.format(
36424                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36425                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36426             this.parentMenu.autoWidth();
36427         }
36428     },
36429
36430     // private
36431     handleClick : function(e){
36432         if(!this.href){ // if no link defined, stop the event automatically
36433             e.stopEvent();
36434         }
36435         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36436     },
36437
36438     // private
36439     activate : function(autoExpand){
36440         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36441             this.focus();
36442             if(autoExpand){
36443                 this.expandMenu();
36444             }
36445         }
36446         return true;
36447     },
36448
36449     // private
36450     shouldDeactivate : function(e){
36451         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36452             if(this.menu && this.menu.isVisible()){
36453                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36454             }
36455             return true;
36456         }
36457         return false;
36458     },
36459
36460     // private
36461     deactivate : function(){
36462         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36463         this.hideMenu();
36464     },
36465
36466     // private
36467     expandMenu : function(autoActivate){
36468         if(!this.disabled && this.menu){
36469             clearTimeout(this.hideTimer);
36470             delete this.hideTimer;
36471             if(!this.menu.isVisible() && !this.showTimer){
36472                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36473             }else if (this.menu.isVisible() && autoActivate){
36474                 this.menu.tryActivate(0, 1);
36475             }
36476         }
36477     },
36478
36479     // private
36480     deferExpand : function(autoActivate){
36481         delete this.showTimer;
36482         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36483         if(autoActivate){
36484             this.menu.tryActivate(0, 1);
36485         }
36486     },
36487
36488     // private
36489     hideMenu : function(){
36490         clearTimeout(this.showTimer);
36491         delete this.showTimer;
36492         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36493             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36494         }
36495     },
36496
36497     // private
36498     deferHide : function(){
36499         delete this.hideTimer;
36500         this.menu.hide();
36501     }
36502 });/*
36503  * Based on:
36504  * Ext JS Library 1.1.1
36505  * Copyright(c) 2006-2007, Ext JS, LLC.
36506  *
36507  * Originally Released Under LGPL - original licence link has changed is not relivant.
36508  *
36509  * Fork - LGPL
36510  * <script type="text/javascript">
36511  */
36512  
36513 /**
36514  * @class Roo.menu.CheckItem
36515  * @extends Roo.menu.Item
36516  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36517  * @constructor
36518  * Creates a new CheckItem
36519  * @param {Object} config Configuration options
36520  */
36521 Roo.menu.CheckItem = function(config){
36522     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36523     this.addEvents({
36524         /**
36525          * @event beforecheckchange
36526          * Fires before the checked value is set, providing an opportunity to cancel if needed
36527          * @param {Roo.menu.CheckItem} this
36528          * @param {Boolean} checked The new checked value that will be set
36529          */
36530         "beforecheckchange" : true,
36531         /**
36532          * @event checkchange
36533          * Fires after the checked value has been set
36534          * @param {Roo.menu.CheckItem} this
36535          * @param {Boolean} checked The checked value that was set
36536          */
36537         "checkchange" : true
36538     });
36539     if(this.checkHandler){
36540         this.on('checkchange', this.checkHandler, this.scope);
36541     }
36542 };
36543 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36544     /**
36545      * @cfg {String} group
36546      * All check items with the same group name will automatically be grouped into a single-select
36547      * radio button group (defaults to '')
36548      */
36549     /**
36550      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36551      */
36552     itemCls : "x-menu-item x-menu-check-item",
36553     /**
36554      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36555      */
36556     groupClass : "x-menu-group-item",
36557
36558     /**
36559      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36560      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36561      * initialized with checked = true will be rendered as checked.
36562      */
36563     checked: false,
36564
36565     // private
36566     ctype: "Roo.menu.CheckItem",
36567
36568     // private
36569     onRender : function(c){
36570         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36571         if(this.group){
36572             this.el.addClass(this.groupClass);
36573         }
36574         Roo.menu.MenuMgr.registerCheckable(this);
36575         if(this.checked){
36576             this.checked = false;
36577             this.setChecked(true, true);
36578         }
36579     },
36580
36581     // private
36582     destroy : function(){
36583         if(this.rendered){
36584             Roo.menu.MenuMgr.unregisterCheckable(this);
36585         }
36586         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36587     },
36588
36589     /**
36590      * Set the checked state of this item
36591      * @param {Boolean} checked The new checked value
36592      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36593      */
36594     setChecked : function(state, suppressEvent){
36595         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36596             if(this.container){
36597                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36598             }
36599             this.checked = state;
36600             if(suppressEvent !== true){
36601                 this.fireEvent("checkchange", this, state);
36602             }
36603         }
36604     },
36605
36606     // private
36607     handleClick : function(e){
36608        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36609            this.setChecked(!this.checked);
36610        }
36611        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36612     }
36613 });/*
36614  * Based on:
36615  * Ext JS Library 1.1.1
36616  * Copyright(c) 2006-2007, Ext JS, LLC.
36617  *
36618  * Originally Released Under LGPL - original licence link has changed is not relivant.
36619  *
36620  * Fork - LGPL
36621  * <script type="text/javascript">
36622  */
36623  
36624 /**
36625  * @class Roo.menu.DateItem
36626  * @extends Roo.menu.Adapter
36627  * A menu item that wraps the {@link Roo.DatPicker} component.
36628  * @constructor
36629  * Creates a new DateItem
36630  * @param {Object} config Configuration options
36631  */
36632 Roo.menu.DateItem = function(config){
36633     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36634     /** The Roo.DatePicker object @type Roo.DatePicker */
36635     this.picker = this.component;
36636     this.addEvents({select: true});
36637     
36638     this.picker.on("render", function(picker){
36639         picker.getEl().swallowEvent("click");
36640         picker.container.addClass("x-menu-date-item");
36641     });
36642
36643     this.picker.on("select", this.onSelect, this);
36644 };
36645
36646 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36647     // private
36648     onSelect : function(picker, date){
36649         this.fireEvent("select", this, date, picker);
36650         Roo.menu.DateItem.superclass.handleClick.call(this);
36651     }
36652 });/*
36653  * Based on:
36654  * Ext JS Library 1.1.1
36655  * Copyright(c) 2006-2007, Ext JS, LLC.
36656  *
36657  * Originally Released Under LGPL - original licence link has changed is not relivant.
36658  *
36659  * Fork - LGPL
36660  * <script type="text/javascript">
36661  */
36662  
36663 /**
36664  * @class Roo.menu.ColorItem
36665  * @extends Roo.menu.Adapter
36666  * A menu item that wraps the {@link Roo.ColorPalette} component.
36667  * @constructor
36668  * Creates a new ColorItem
36669  * @param {Object} config Configuration options
36670  */
36671 Roo.menu.ColorItem = function(config){
36672     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36673     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36674     this.palette = this.component;
36675     this.relayEvents(this.palette, ["select"]);
36676     if(this.selectHandler){
36677         this.on('select', this.selectHandler, this.scope);
36678     }
36679 };
36680 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36681  * Based on:
36682  * Ext JS Library 1.1.1
36683  * Copyright(c) 2006-2007, Ext JS, LLC.
36684  *
36685  * Originally Released Under LGPL - original licence link has changed is not relivant.
36686  *
36687  * Fork - LGPL
36688  * <script type="text/javascript">
36689  */
36690  
36691
36692 /**
36693  * @class Roo.menu.DateMenu
36694  * @extends Roo.menu.Menu
36695  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36696  * @constructor
36697  * Creates a new DateMenu
36698  * @param {Object} config Configuration options
36699  */
36700 Roo.menu.DateMenu = function(config){
36701     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36702     this.plain = true;
36703     var di = new Roo.menu.DateItem(config);
36704     this.add(di);
36705     /**
36706      * The {@link Roo.DatePicker} instance for this DateMenu
36707      * @type DatePicker
36708      */
36709     this.picker = di.picker;
36710     /**
36711      * @event select
36712      * @param {DatePicker} picker
36713      * @param {Date} date
36714      */
36715     this.relayEvents(di, ["select"]);
36716     this.on('beforeshow', function(){
36717         if(this.picker){
36718             this.picker.hideMonthPicker(false);
36719         }
36720     }, this);
36721 };
36722 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36723     cls:'x-date-menu'
36724 });/*
36725  * Based on:
36726  * Ext JS Library 1.1.1
36727  * Copyright(c) 2006-2007, Ext JS, LLC.
36728  *
36729  * Originally Released Under LGPL - original licence link has changed is not relivant.
36730  *
36731  * Fork - LGPL
36732  * <script type="text/javascript">
36733  */
36734  
36735
36736 /**
36737  * @class Roo.menu.ColorMenu
36738  * @extends Roo.menu.Menu
36739  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36740  * @constructor
36741  * Creates a new ColorMenu
36742  * @param {Object} config Configuration options
36743  */
36744 Roo.menu.ColorMenu = function(config){
36745     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36746     this.plain = true;
36747     var ci = new Roo.menu.ColorItem(config);
36748     this.add(ci);
36749     /**
36750      * The {@link Roo.ColorPalette} instance for this ColorMenu
36751      * @type ColorPalette
36752      */
36753     this.palette = ci.palette;
36754     /**
36755      * @event select
36756      * @param {ColorPalette} palette
36757      * @param {String} color
36758      */
36759     this.relayEvents(ci, ["select"]);
36760 };
36761 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36762  * Based on:
36763  * Ext JS Library 1.1.1
36764  * Copyright(c) 2006-2007, Ext JS, LLC.
36765  *
36766  * Originally Released Under LGPL - original licence link has changed is not relivant.
36767  *
36768  * Fork - LGPL
36769  * <script type="text/javascript">
36770  */
36771  
36772 /**
36773  * @class Roo.form.Field
36774  * @extends Roo.BoxComponent
36775  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36776  * @constructor
36777  * Creates a new Field
36778  * @param {Object} config Configuration options
36779  */
36780 Roo.form.Field = function(config){
36781     Roo.form.Field.superclass.constructor.call(this, config);
36782 };
36783
36784 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36785     /**
36786      * @cfg {String} fieldLabel Label to use when rendering a form.
36787      */
36788        /**
36789      * @cfg {String} qtip Mouse over tip
36790      */
36791      
36792     /**
36793      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36794      */
36795     invalidClass : "x-form-invalid",
36796     /**
36797      * @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")
36798      */
36799     invalidText : "The value in this field is invalid",
36800     /**
36801      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36802      */
36803     focusClass : "x-form-focus",
36804     /**
36805      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36806       automatic validation (defaults to "keyup").
36807      */
36808     validationEvent : "keyup",
36809     /**
36810      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36811      */
36812     validateOnBlur : true,
36813     /**
36814      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36815      */
36816     validationDelay : 250,
36817     /**
36818      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36819      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36820      */
36821     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36822     /**
36823      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36824      */
36825     fieldClass : "x-form-field",
36826     /**
36827      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36828      *<pre>
36829 Value         Description
36830 -----------   ----------------------------------------------------------------------
36831 qtip          Display a quick tip when the user hovers over the field
36832 title         Display a default browser title attribute popup
36833 under         Add a block div beneath the field containing the error text
36834 side          Add an error icon to the right of the field with a popup on hover
36835 [element id]  Add the error text directly to the innerHTML of the specified element
36836 </pre>
36837      */
36838     msgTarget : 'qtip',
36839     /**
36840      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36841      */
36842     msgFx : 'normal',
36843
36844     /**
36845      * @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.
36846      */
36847     readOnly : false,
36848
36849     /**
36850      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36851      */
36852     disabled : false,
36853
36854     /**
36855      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36856      */
36857     inputType : undefined,
36858     
36859     /**
36860      * @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).
36861          */
36862         tabIndex : undefined,
36863         
36864     // private
36865     isFormField : true,
36866
36867     // private
36868     hasFocus : false,
36869     /**
36870      * @property {Roo.Element} fieldEl
36871      * Element Containing the rendered Field (with label etc.)
36872      */
36873     /**
36874      * @cfg {Mixed} value A value to initialize this field with.
36875      */
36876     value : undefined,
36877
36878     /**
36879      * @cfg {String} name The field's HTML name attribute.
36880      */
36881     /**
36882      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36883      */
36884
36885         // private ??
36886         initComponent : function(){
36887         Roo.form.Field.superclass.initComponent.call(this);
36888         this.addEvents({
36889             /**
36890              * @event focus
36891              * Fires when this field receives input focus.
36892              * @param {Roo.form.Field} this
36893              */
36894             focus : true,
36895             /**
36896              * @event blur
36897              * Fires when this field loses input focus.
36898              * @param {Roo.form.Field} this
36899              */
36900             blur : true,
36901             /**
36902              * @event specialkey
36903              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36904              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36905              * @param {Roo.form.Field} this
36906              * @param {Roo.EventObject} e The event object
36907              */
36908             specialkey : true,
36909             /**
36910              * @event change
36911              * Fires just before the field blurs if the field value has changed.
36912              * @param {Roo.form.Field} this
36913              * @param {Mixed} newValue The new value
36914              * @param {Mixed} oldValue The original value
36915              */
36916             change : true,
36917             /**
36918              * @event invalid
36919              * Fires after the field has been marked as invalid.
36920              * @param {Roo.form.Field} this
36921              * @param {String} msg The validation message
36922              */
36923             invalid : true,
36924             /**
36925              * @event valid
36926              * Fires after the field has been validated with no errors.
36927              * @param {Roo.form.Field} this
36928              */
36929             valid : true,
36930              /**
36931              * @event keyup
36932              * Fires after the key up
36933              * @param {Roo.form.Field} this
36934              * @param {Roo.EventObject}  e The event Object
36935              */
36936             keyup : true
36937         });
36938     },
36939
36940     /**
36941      * Returns the name attribute of the field if available
36942      * @return {String} name The field name
36943      */
36944     getName: function(){
36945          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36946     },
36947
36948     // private
36949     onRender : function(ct, position){
36950         Roo.form.Field.superclass.onRender.call(this, ct, position);
36951         if(!this.el){
36952             var cfg = this.getAutoCreate();
36953             if(!cfg.name){
36954                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36955             }
36956             if (!cfg.name.length) {
36957                 delete cfg.name;
36958             }
36959             if(this.inputType){
36960                 cfg.type = this.inputType;
36961             }
36962             this.el = ct.createChild(cfg, position);
36963         }
36964         var type = this.el.dom.type;
36965         if(type){
36966             if(type == 'password'){
36967                 type = 'text';
36968             }
36969             this.el.addClass('x-form-'+type);
36970         }
36971         if(this.readOnly){
36972             this.el.dom.readOnly = true;
36973         }
36974         if(this.tabIndex !== undefined){
36975             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36976         }
36977
36978         this.el.addClass([this.fieldClass, this.cls]);
36979         this.initValue();
36980     },
36981
36982     /**
36983      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36984      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36985      * @return {Roo.form.Field} this
36986      */
36987     applyTo : function(target){
36988         this.allowDomMove = false;
36989         this.el = Roo.get(target);
36990         this.render(this.el.dom.parentNode);
36991         return this;
36992     },
36993
36994     // private
36995     initValue : function(){
36996         if(this.value !== undefined){
36997             this.setValue(this.value);
36998         }else if(this.el.dom.value.length > 0){
36999             this.setValue(this.el.dom.value);
37000         }
37001     },
37002
37003     /**
37004      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37005      */
37006     isDirty : function() {
37007         if(this.disabled) {
37008             return false;
37009         }
37010         return String(this.getValue()) !== String(this.originalValue);
37011     },
37012
37013     // private
37014     afterRender : function(){
37015         Roo.form.Field.superclass.afterRender.call(this);
37016         this.initEvents();
37017     },
37018
37019     // private
37020     fireKey : function(e){
37021         //Roo.log('field ' + e.getKey());
37022         if(e.isNavKeyPress()){
37023             this.fireEvent("specialkey", this, e);
37024         }
37025     },
37026
37027     /**
37028      * Resets the current field value to the originally loaded value and clears any validation messages
37029      */
37030     reset : function(){
37031         this.setValue(this.originalValue);
37032         this.clearInvalid();
37033     },
37034
37035     // private
37036     initEvents : function(){
37037         // safari killled keypress - so keydown is now used..
37038         this.el.on("keydown" , this.fireKey,  this);
37039         this.el.on("focus", this.onFocus,  this);
37040         this.el.on("blur", this.onBlur,  this);
37041         this.el.relayEvent('keyup', this);
37042
37043         // reference to original value for reset
37044         this.originalValue = this.getValue();
37045     },
37046
37047     // private
37048     onFocus : function(){
37049         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37050             this.el.addClass(this.focusClass);
37051         }
37052         if(!this.hasFocus){
37053             this.hasFocus = true;
37054             this.startValue = this.getValue();
37055             this.fireEvent("focus", this);
37056         }
37057     },
37058
37059     beforeBlur : Roo.emptyFn,
37060
37061     // private
37062     onBlur : function(){
37063         this.beforeBlur();
37064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37065             this.el.removeClass(this.focusClass);
37066         }
37067         this.hasFocus = false;
37068         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37069             this.validate();
37070         }
37071         var v = this.getValue();
37072         if(String(v) !== String(this.startValue)){
37073             this.fireEvent('change', this, v, this.startValue);
37074         }
37075         this.fireEvent("blur", this);
37076     },
37077
37078     /**
37079      * Returns whether or not the field value is currently valid
37080      * @param {Boolean} preventMark True to disable marking the field invalid
37081      * @return {Boolean} True if the value is valid, else false
37082      */
37083     isValid : function(preventMark){
37084         if(this.disabled){
37085             return true;
37086         }
37087         var restore = this.preventMark;
37088         this.preventMark = preventMark === true;
37089         var v = this.validateValue(this.processValue(this.getRawValue()));
37090         this.preventMark = restore;
37091         return v;
37092     },
37093
37094     /**
37095      * Validates the field value
37096      * @return {Boolean} True if the value is valid, else false
37097      */
37098     validate : function(){
37099         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37100             this.clearInvalid();
37101             return true;
37102         }
37103         return false;
37104     },
37105
37106     processValue : function(value){
37107         return value;
37108     },
37109
37110     // private
37111     // Subclasses should provide the validation implementation by overriding this
37112     validateValue : function(value){
37113         return true;
37114     },
37115
37116     /**
37117      * Mark this field as invalid
37118      * @param {String} msg The validation message
37119      */
37120     markInvalid : function(msg){
37121         if(!this.rendered || this.preventMark){ // not rendered
37122             return;
37123         }
37124         
37125         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37126         
37127         obj.el.addClass(this.invalidClass);
37128         msg = msg || this.invalidText;
37129         switch(this.msgTarget){
37130             case 'qtip':
37131                 obj.el.dom.qtip = msg;
37132                 obj.el.dom.qclass = 'x-form-invalid-tip';
37133                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37134                     Roo.QuickTips.enable();
37135                 }
37136                 break;
37137             case 'title':
37138                 this.el.dom.title = msg;
37139                 break;
37140             case 'under':
37141                 if(!this.errorEl){
37142                     var elp = this.el.findParent('.x-form-element', 5, true);
37143                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37144                     this.errorEl.setWidth(elp.getWidth(true)-20);
37145                 }
37146                 this.errorEl.update(msg);
37147                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37148                 break;
37149             case 'side':
37150                 if(!this.errorIcon){
37151                     var elp = this.el.findParent('.x-form-element', 5, true);
37152                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37153                 }
37154                 this.alignErrorIcon();
37155                 this.errorIcon.dom.qtip = msg;
37156                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37157                 this.errorIcon.show();
37158                 this.on('resize', this.alignErrorIcon, this);
37159                 break;
37160             default:
37161                 var t = Roo.getDom(this.msgTarget);
37162                 t.innerHTML = msg;
37163                 t.style.display = this.msgDisplay;
37164                 break;
37165         }
37166         this.fireEvent('invalid', this, msg);
37167     },
37168
37169     // private
37170     alignErrorIcon : function(){
37171         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37172     },
37173
37174     /**
37175      * Clear any invalid styles/messages for this field
37176      */
37177     clearInvalid : function(){
37178         if(!this.rendered || this.preventMark){ // not rendered
37179             return;
37180         }
37181         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37182         
37183         obj.el.removeClass(this.invalidClass);
37184         switch(this.msgTarget){
37185             case 'qtip':
37186                 obj.el.dom.qtip = '';
37187                 break;
37188             case 'title':
37189                 this.el.dom.title = '';
37190                 break;
37191             case 'under':
37192                 if(this.errorEl){
37193                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37194                 }
37195                 break;
37196             case 'side':
37197                 if(this.errorIcon){
37198                     this.errorIcon.dom.qtip = '';
37199                     this.errorIcon.hide();
37200                     this.un('resize', this.alignErrorIcon, this);
37201                 }
37202                 break;
37203             default:
37204                 var t = Roo.getDom(this.msgTarget);
37205                 t.innerHTML = '';
37206                 t.style.display = 'none';
37207                 break;
37208         }
37209         this.fireEvent('valid', this);
37210     },
37211
37212     /**
37213      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37214      * @return {Mixed} value The field value
37215      */
37216     getRawValue : function(){
37217         var v = this.el.getValue();
37218         
37219         return v;
37220     },
37221
37222     /**
37223      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37224      * @return {Mixed} value The field value
37225      */
37226     getValue : function(){
37227         var v = this.el.getValue();
37228          
37229         return v;
37230     },
37231
37232     /**
37233      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37234      * @param {Mixed} value The value to set
37235      */
37236     setRawValue : function(v){
37237         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37238     },
37239
37240     /**
37241      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37242      * @param {Mixed} value The value to set
37243      */
37244     setValue : function(v){
37245         this.value = v;
37246         if(this.rendered){
37247             this.el.dom.value = (v === null || v === undefined ? '' : v);
37248              this.validate();
37249         }
37250     },
37251
37252     adjustSize : function(w, h){
37253         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37254         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37255         return s;
37256     },
37257
37258     adjustWidth : function(tag, w){
37259         tag = tag.toLowerCase();
37260         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37261             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37262                 if(tag == 'input'){
37263                     return w + 2;
37264                 }
37265                 if(tag == 'textarea'){
37266                     return w-2;
37267                 }
37268             }else if(Roo.isOpera){
37269                 if(tag == 'input'){
37270                     return w + 2;
37271                 }
37272                 if(tag == 'textarea'){
37273                     return w-2;
37274                 }
37275             }
37276         }
37277         return w;
37278     }
37279 });
37280
37281
37282 // anything other than normal should be considered experimental
37283 Roo.form.Field.msgFx = {
37284     normal : {
37285         show: function(msgEl, f){
37286             msgEl.setDisplayed('block');
37287         },
37288
37289         hide : function(msgEl, f){
37290             msgEl.setDisplayed(false).update('');
37291         }
37292     },
37293
37294     slide : {
37295         show: function(msgEl, f){
37296             msgEl.slideIn('t', {stopFx:true});
37297         },
37298
37299         hide : function(msgEl, f){
37300             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37301         }
37302     },
37303
37304     slideRight : {
37305         show: function(msgEl, f){
37306             msgEl.fixDisplay();
37307             msgEl.alignTo(f.el, 'tl-tr');
37308             msgEl.slideIn('l', {stopFx:true});
37309         },
37310
37311         hide : function(msgEl, f){
37312             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37313         }
37314     }
37315 };/*
37316  * Based on:
37317  * Ext JS Library 1.1.1
37318  * Copyright(c) 2006-2007, Ext JS, LLC.
37319  *
37320  * Originally Released Under LGPL - original licence link has changed is not relivant.
37321  *
37322  * Fork - LGPL
37323  * <script type="text/javascript">
37324  */
37325  
37326
37327 /**
37328  * @class Roo.form.TextField
37329  * @extends Roo.form.Field
37330  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37331  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37332  * @constructor
37333  * Creates a new TextField
37334  * @param {Object} config Configuration options
37335  */
37336 Roo.form.TextField = function(config){
37337     Roo.form.TextField.superclass.constructor.call(this, config);
37338     this.addEvents({
37339         /**
37340          * @event autosize
37341          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37342          * according to the default logic, but this event provides a hook for the developer to apply additional
37343          * logic at runtime to resize the field if needed.
37344              * @param {Roo.form.Field} this This text field
37345              * @param {Number} width The new field width
37346              */
37347         autosize : true
37348     });
37349 };
37350
37351 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37352     /**
37353      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37354      */
37355     grow : false,
37356     /**
37357      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37358      */
37359     growMin : 30,
37360     /**
37361      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37362      */
37363     growMax : 800,
37364     /**
37365      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37366      */
37367     vtype : null,
37368     /**
37369      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37370      */
37371     maskRe : null,
37372     /**
37373      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37374      */
37375     disableKeyFilter : false,
37376     /**
37377      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37378      */
37379     allowBlank : true,
37380     /**
37381      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37382      */
37383     minLength : 0,
37384     /**
37385      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37386      */
37387     maxLength : Number.MAX_VALUE,
37388     /**
37389      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37390      */
37391     minLengthText : "The minimum length for this field is {0}",
37392     /**
37393      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37394      */
37395     maxLengthText : "The maximum length for this field is {0}",
37396     /**
37397      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37398      */
37399     selectOnFocus : false,
37400     /**
37401      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37402      */
37403     blankText : "This field is required",
37404     /**
37405      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37406      * If available, this function will be called only after the basic validators all return true, and will be passed the
37407      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37408      */
37409     validator : null,
37410     /**
37411      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37412      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37413      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37414      */
37415     regex : null,
37416     /**
37417      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37418      */
37419     regexText : "",
37420     /**
37421      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37422      */
37423     emptyText : null,
37424    
37425
37426     // private
37427     initEvents : function()
37428     {
37429         if (this.emptyText) {
37430             this.el.attr('placeholder', this.emptyText);
37431         }
37432         
37433         Roo.form.TextField.superclass.initEvents.call(this);
37434         if(this.validationEvent == 'keyup'){
37435             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37436             this.el.on('keyup', this.filterValidation, this);
37437         }
37438         else if(this.validationEvent !== false){
37439             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37440         }
37441         
37442         if(this.selectOnFocus){
37443             this.on("focus", this.preFocus, this);
37444             
37445         }
37446         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37447             this.el.on("keypress", this.filterKeys, this);
37448         }
37449         if(this.grow){
37450             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37451             this.el.on("click", this.autoSize,  this);
37452         }
37453         if(this.el.is('input[type=password]') && Roo.isSafari){
37454             this.el.on('keydown', this.SafariOnKeyDown, this);
37455         }
37456     },
37457
37458     processValue : function(value){
37459         if(this.stripCharsRe){
37460             var newValue = value.replace(this.stripCharsRe, '');
37461             if(newValue !== value){
37462                 this.setRawValue(newValue);
37463                 return newValue;
37464             }
37465         }
37466         return value;
37467     },
37468
37469     filterValidation : function(e){
37470         if(!e.isNavKeyPress()){
37471             this.validationTask.delay(this.validationDelay);
37472         }
37473     },
37474
37475     // private
37476     onKeyUp : function(e){
37477         if(!e.isNavKeyPress()){
37478             this.autoSize();
37479         }
37480     },
37481
37482     /**
37483      * Resets the current field value to the originally-loaded value and clears any validation messages.
37484      *  
37485      */
37486     reset : function(){
37487         Roo.form.TextField.superclass.reset.call(this);
37488        
37489     },
37490
37491     
37492     // private
37493     preFocus : function(){
37494         
37495         if(this.selectOnFocus){
37496             this.el.dom.select();
37497         }
37498     },
37499
37500     
37501     // private
37502     filterKeys : function(e){
37503         var k = e.getKey();
37504         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37505             return;
37506         }
37507         var c = e.getCharCode(), cc = String.fromCharCode(c);
37508         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37509             return;
37510         }
37511         if(!this.maskRe.test(cc)){
37512             e.stopEvent();
37513         }
37514     },
37515
37516     setValue : function(v){
37517         
37518         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37519         
37520         this.autoSize();
37521     },
37522
37523     /**
37524      * Validates a value according to the field's validation rules and marks the field as invalid
37525      * if the validation fails
37526      * @param {Mixed} value The value to validate
37527      * @return {Boolean} True if the value is valid, else false
37528      */
37529     validateValue : function(value){
37530         if(value.length < 1)  { // if it's blank
37531              if(this.allowBlank){
37532                 this.clearInvalid();
37533                 return true;
37534              }else{
37535                 this.markInvalid(this.blankText);
37536                 return false;
37537              }
37538         }
37539         if(value.length < this.minLength){
37540             this.markInvalid(String.format(this.minLengthText, this.minLength));
37541             return false;
37542         }
37543         if(value.length > this.maxLength){
37544             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37545             return false;
37546         }
37547         if(this.vtype){
37548             var vt = Roo.form.VTypes;
37549             if(!vt[this.vtype](value, this)){
37550                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37551                 return false;
37552             }
37553         }
37554         if(typeof this.validator == "function"){
37555             var msg = this.validator(value);
37556             if(msg !== true){
37557                 this.markInvalid(msg);
37558                 return false;
37559             }
37560         }
37561         if(this.regex && !this.regex.test(value)){
37562             this.markInvalid(this.regexText);
37563             return false;
37564         }
37565         return true;
37566     },
37567
37568     /**
37569      * Selects text in this field
37570      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37571      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37572      */
37573     selectText : function(start, end){
37574         var v = this.getRawValue();
37575         if(v.length > 0){
37576             start = start === undefined ? 0 : start;
37577             end = end === undefined ? v.length : end;
37578             var d = this.el.dom;
37579             if(d.setSelectionRange){
37580                 d.setSelectionRange(start, end);
37581             }else if(d.createTextRange){
37582                 var range = d.createTextRange();
37583                 range.moveStart("character", start);
37584                 range.moveEnd("character", v.length-end);
37585                 range.select();
37586             }
37587         }
37588     },
37589
37590     /**
37591      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37592      * This only takes effect if grow = true, and fires the autosize event.
37593      */
37594     autoSize : function(){
37595         if(!this.grow || !this.rendered){
37596             return;
37597         }
37598         if(!this.metrics){
37599             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37600         }
37601         var el = this.el;
37602         var v = el.dom.value;
37603         var d = document.createElement('div');
37604         d.appendChild(document.createTextNode(v));
37605         v = d.innerHTML;
37606         d = null;
37607         v += "&#160;";
37608         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37609         this.el.setWidth(w);
37610         this.fireEvent("autosize", this, w);
37611     },
37612     
37613     // private
37614     SafariOnKeyDown : function(event)
37615     {
37616         // this is a workaround for a password hang bug on chrome/ webkit.
37617         
37618         var isSelectAll = false;
37619         
37620         if(this.el.dom.selectionEnd > 0){
37621             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37622         }
37623         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37624             event.preventDefault();
37625             this.setValue('');
37626             return;
37627         }
37628         
37629         if(isSelectAll){ // backspace and delete key
37630             
37631             event.preventDefault();
37632             // this is very hacky as keydown always get's upper case.
37633             //
37634             var cc = String.fromCharCode(event.getCharCode());
37635             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37636             
37637         }
37638         
37639         
37640     }
37641 });/*
37642  * Based on:
37643  * Ext JS Library 1.1.1
37644  * Copyright(c) 2006-2007, Ext JS, LLC.
37645  *
37646  * Originally Released Under LGPL - original licence link has changed is not relivant.
37647  *
37648  * Fork - LGPL
37649  * <script type="text/javascript">
37650  */
37651  
37652 /**
37653  * @class Roo.form.Hidden
37654  * @extends Roo.form.TextField
37655  * Simple Hidden element used on forms 
37656  * 
37657  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37658  * 
37659  * @constructor
37660  * Creates a new Hidden form element.
37661  * @param {Object} config Configuration options
37662  */
37663
37664
37665
37666 // easy hidden field...
37667 Roo.form.Hidden = function(config){
37668     Roo.form.Hidden.superclass.constructor.call(this, config);
37669 };
37670   
37671 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37672     fieldLabel:      '',
37673     inputType:      'hidden',
37674     width:          50,
37675     allowBlank:     true,
37676     labelSeparator: '',
37677     hidden:         true,
37678     itemCls :       'x-form-item-display-none'
37679
37680
37681 });
37682
37683
37684 /*
37685  * Based on:
37686  * Ext JS Library 1.1.1
37687  * Copyright(c) 2006-2007, Ext JS, LLC.
37688  *
37689  * Originally Released Under LGPL - original licence link has changed is not relivant.
37690  *
37691  * Fork - LGPL
37692  * <script type="text/javascript">
37693  */
37694  
37695 /**
37696  * @class Roo.form.TriggerField
37697  * @extends Roo.form.TextField
37698  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37699  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37700  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37701  * for which you can provide a custom implementation.  For example:
37702  * <pre><code>
37703 var trigger = new Roo.form.TriggerField();
37704 trigger.onTriggerClick = myTriggerFn;
37705 trigger.applyTo('my-field');
37706 </code></pre>
37707  *
37708  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37709  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37710  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37711  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37712  * @constructor
37713  * Create a new TriggerField.
37714  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37715  * to the base TextField)
37716  */
37717 Roo.form.TriggerField = function(config){
37718     this.mimicing = false;
37719     Roo.form.TriggerField.superclass.constructor.call(this, config);
37720 };
37721
37722 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37723     /**
37724      * @cfg {String} triggerClass A CSS class to apply to the trigger
37725      */
37726     /**
37727      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37728      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37729      */
37730     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37731     /**
37732      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37733      */
37734     hideTrigger:false,
37735
37736     /** @cfg {Boolean} grow @hide */
37737     /** @cfg {Number} growMin @hide */
37738     /** @cfg {Number} growMax @hide */
37739
37740     /**
37741      * @hide 
37742      * @method
37743      */
37744     autoSize: Roo.emptyFn,
37745     // private
37746     monitorTab : true,
37747     // private
37748     deferHeight : true,
37749
37750     
37751     actionMode : 'wrap',
37752     // private
37753     onResize : function(w, h){
37754         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37755         if(typeof w == 'number'){
37756             var x = w - this.trigger.getWidth();
37757             this.el.setWidth(this.adjustWidth('input', x));
37758             this.trigger.setStyle('left', x+'px');
37759         }
37760     },
37761
37762     // private
37763     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37764
37765     // private
37766     getResizeEl : function(){
37767         return this.wrap;
37768     },
37769
37770     // private
37771     getPositionEl : function(){
37772         return this.wrap;
37773     },
37774
37775     // private
37776     alignErrorIcon : function(){
37777         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37778     },
37779
37780     // private
37781     onRender : function(ct, position){
37782         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37783         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37784         this.trigger = this.wrap.createChild(this.triggerConfig ||
37785                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37786         if(this.hideTrigger){
37787             this.trigger.setDisplayed(false);
37788         }
37789         this.initTrigger();
37790         if(!this.width){
37791             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37792         }
37793     },
37794
37795     // private
37796     initTrigger : function(){
37797         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37798         this.trigger.addClassOnOver('x-form-trigger-over');
37799         this.trigger.addClassOnClick('x-form-trigger-click');
37800     },
37801
37802     // private
37803     onDestroy : function(){
37804         if(this.trigger){
37805             this.trigger.removeAllListeners();
37806             this.trigger.remove();
37807         }
37808         if(this.wrap){
37809             this.wrap.remove();
37810         }
37811         Roo.form.TriggerField.superclass.onDestroy.call(this);
37812     },
37813
37814     // private
37815     onFocus : function(){
37816         Roo.form.TriggerField.superclass.onFocus.call(this);
37817         if(!this.mimicing){
37818             this.wrap.addClass('x-trigger-wrap-focus');
37819             this.mimicing = true;
37820             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37821             if(this.monitorTab){
37822                 this.el.on("keydown", this.checkTab, this);
37823             }
37824         }
37825     },
37826
37827     // private
37828     checkTab : function(e){
37829         if(e.getKey() == e.TAB){
37830             this.triggerBlur();
37831         }
37832     },
37833
37834     // private
37835     onBlur : function(){
37836         // do nothing
37837     },
37838
37839     // private
37840     mimicBlur : function(e, t){
37841         if(!this.wrap.contains(t) && this.validateBlur()){
37842             this.triggerBlur();
37843         }
37844     },
37845
37846     // private
37847     triggerBlur : function(){
37848         this.mimicing = false;
37849         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37850         if(this.monitorTab){
37851             this.el.un("keydown", this.checkTab, this);
37852         }
37853         this.wrap.removeClass('x-trigger-wrap-focus');
37854         Roo.form.TriggerField.superclass.onBlur.call(this);
37855     },
37856
37857     // private
37858     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37859     validateBlur : function(e, t){
37860         return true;
37861     },
37862
37863     // private
37864     onDisable : function(){
37865         Roo.form.TriggerField.superclass.onDisable.call(this);
37866         if(this.wrap){
37867             this.wrap.addClass('x-item-disabled');
37868         }
37869     },
37870
37871     // private
37872     onEnable : function(){
37873         Roo.form.TriggerField.superclass.onEnable.call(this);
37874         if(this.wrap){
37875             this.wrap.removeClass('x-item-disabled');
37876         }
37877     },
37878
37879     // private
37880     onShow : function(){
37881         var ae = this.getActionEl();
37882         
37883         if(ae){
37884             ae.dom.style.display = '';
37885             ae.dom.style.visibility = 'visible';
37886         }
37887     },
37888
37889     // private
37890     
37891     onHide : function(){
37892         var ae = this.getActionEl();
37893         ae.dom.style.display = 'none';
37894     },
37895
37896     /**
37897      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37898      * by an implementing function.
37899      * @method
37900      * @param {EventObject} e
37901      */
37902     onTriggerClick : Roo.emptyFn
37903 });
37904
37905 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37906 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37907 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37908 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37909     initComponent : function(){
37910         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37911
37912         this.triggerConfig = {
37913             tag:'span', cls:'x-form-twin-triggers', cn:[
37914             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37915             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37916         ]};
37917     },
37918
37919     getTrigger : function(index){
37920         return this.triggers[index];
37921     },
37922
37923     initTrigger : function(){
37924         var ts = this.trigger.select('.x-form-trigger', true);
37925         this.wrap.setStyle('overflow', 'hidden');
37926         var triggerField = this;
37927         ts.each(function(t, all, index){
37928             t.hide = function(){
37929                 var w = triggerField.wrap.getWidth();
37930                 this.dom.style.display = 'none';
37931                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37932             };
37933             t.show = function(){
37934                 var w = triggerField.wrap.getWidth();
37935                 this.dom.style.display = '';
37936                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37937             };
37938             var triggerIndex = 'Trigger'+(index+1);
37939
37940             if(this['hide'+triggerIndex]){
37941                 t.dom.style.display = 'none';
37942             }
37943             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37944             t.addClassOnOver('x-form-trigger-over');
37945             t.addClassOnClick('x-form-trigger-click');
37946         }, this);
37947         this.triggers = ts.elements;
37948     },
37949
37950     onTrigger1Click : Roo.emptyFn,
37951     onTrigger2Click : Roo.emptyFn
37952 });/*
37953  * Based on:
37954  * Ext JS Library 1.1.1
37955  * Copyright(c) 2006-2007, Ext JS, LLC.
37956  *
37957  * Originally Released Under LGPL - original licence link has changed is not relivant.
37958  *
37959  * Fork - LGPL
37960  * <script type="text/javascript">
37961  */
37962  
37963 /**
37964  * @class Roo.form.TextArea
37965  * @extends Roo.form.TextField
37966  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37967  * support for auto-sizing.
37968  * @constructor
37969  * Creates a new TextArea
37970  * @param {Object} config Configuration options
37971  */
37972 Roo.form.TextArea = function(config){
37973     Roo.form.TextArea.superclass.constructor.call(this, config);
37974     // these are provided exchanges for backwards compat
37975     // minHeight/maxHeight were replaced by growMin/growMax to be
37976     // compatible with TextField growing config values
37977     if(this.minHeight !== undefined){
37978         this.growMin = this.minHeight;
37979     }
37980     if(this.maxHeight !== undefined){
37981         this.growMax = this.maxHeight;
37982     }
37983 };
37984
37985 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37986     /**
37987      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37988      */
37989     growMin : 60,
37990     /**
37991      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37992      */
37993     growMax: 1000,
37994     /**
37995      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37996      * in the field (equivalent to setting overflow: hidden, defaults to false)
37997      */
37998     preventScrollbars: false,
37999     /**
38000      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38001      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38002      */
38003
38004     // private
38005     onRender : function(ct, position){
38006         if(!this.el){
38007             this.defaultAutoCreate = {
38008                 tag: "textarea",
38009                 style:"width:300px;height:60px;",
38010                 autocomplete: "off"
38011             };
38012         }
38013         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38014         if(this.grow){
38015             this.textSizeEl = Roo.DomHelper.append(document.body, {
38016                 tag: "pre", cls: "x-form-grow-sizer"
38017             });
38018             if(this.preventScrollbars){
38019                 this.el.setStyle("overflow", "hidden");
38020             }
38021             this.el.setHeight(this.growMin);
38022         }
38023     },
38024
38025     onDestroy : function(){
38026         if(this.textSizeEl){
38027             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38028         }
38029         Roo.form.TextArea.superclass.onDestroy.call(this);
38030     },
38031
38032     // private
38033     onKeyUp : function(e){
38034         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38035             this.autoSize();
38036         }
38037     },
38038
38039     /**
38040      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38041      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38042      */
38043     autoSize : function(){
38044         if(!this.grow || !this.textSizeEl){
38045             return;
38046         }
38047         var el = this.el;
38048         var v = el.dom.value;
38049         var ts = this.textSizeEl;
38050
38051         ts.innerHTML = '';
38052         ts.appendChild(document.createTextNode(v));
38053         v = ts.innerHTML;
38054
38055         Roo.fly(ts).setWidth(this.el.getWidth());
38056         if(v.length < 1){
38057             v = "&#160;&#160;";
38058         }else{
38059             if(Roo.isIE){
38060                 v = v.replace(/\n/g, '<p>&#160;</p>');
38061             }
38062             v += "&#160;\n&#160;";
38063         }
38064         ts.innerHTML = v;
38065         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38066         if(h != this.lastHeight){
38067             this.lastHeight = h;
38068             this.el.setHeight(h);
38069             this.fireEvent("autosize", this, h);
38070         }
38071     }
38072 });/*
38073  * Based on:
38074  * Ext JS Library 1.1.1
38075  * Copyright(c) 2006-2007, Ext JS, LLC.
38076  *
38077  * Originally Released Under LGPL - original licence link has changed is not relivant.
38078  *
38079  * Fork - LGPL
38080  * <script type="text/javascript">
38081  */
38082  
38083
38084 /**
38085  * @class Roo.form.NumberField
38086  * @extends Roo.form.TextField
38087  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38088  * @constructor
38089  * Creates a new NumberField
38090  * @param {Object} config Configuration options
38091  */
38092 Roo.form.NumberField = function(config){
38093     Roo.form.NumberField.superclass.constructor.call(this, config);
38094 };
38095
38096 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38097     /**
38098      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38099      */
38100     fieldClass: "x-form-field x-form-num-field",
38101     /**
38102      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38103      */
38104     allowDecimals : true,
38105     /**
38106      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38107      */
38108     decimalSeparator : ".",
38109     /**
38110      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38111      */
38112     decimalPrecision : 2,
38113     /**
38114      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38115      */
38116     allowNegative : true,
38117     /**
38118      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38119      */
38120     minValue : Number.NEGATIVE_INFINITY,
38121     /**
38122      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38123      */
38124     maxValue : Number.MAX_VALUE,
38125     /**
38126      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38127      */
38128     minText : "The minimum value for this field is {0}",
38129     /**
38130      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38131      */
38132     maxText : "The maximum value for this field is {0}",
38133     /**
38134      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38135      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38136      */
38137     nanText : "{0} is not a valid number",
38138
38139     // private
38140     initEvents : function(){
38141         Roo.form.NumberField.superclass.initEvents.call(this);
38142         var allowed = "0123456789";
38143         if(this.allowDecimals){
38144             allowed += this.decimalSeparator;
38145         }
38146         if(this.allowNegative){
38147             allowed += "-";
38148         }
38149         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38150         var keyPress = function(e){
38151             var k = e.getKey();
38152             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38153                 return;
38154             }
38155             var c = e.getCharCode();
38156             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38157                 e.stopEvent();
38158             }
38159         };
38160         this.el.on("keypress", keyPress, this);
38161     },
38162
38163     // private
38164     validateValue : function(value){
38165         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38166             return false;
38167         }
38168         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38169              return true;
38170         }
38171         var num = this.parseValue(value);
38172         if(isNaN(num)){
38173             this.markInvalid(String.format(this.nanText, value));
38174             return false;
38175         }
38176         if(num < this.minValue){
38177             this.markInvalid(String.format(this.minText, this.minValue));
38178             return false;
38179         }
38180         if(num > this.maxValue){
38181             this.markInvalid(String.format(this.maxText, this.maxValue));
38182             return false;
38183         }
38184         return true;
38185     },
38186
38187     getValue : function(){
38188         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38189     },
38190
38191     // private
38192     parseValue : function(value){
38193         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38194         return isNaN(value) ? '' : value;
38195     },
38196
38197     // private
38198     fixPrecision : function(value){
38199         var nan = isNaN(value);
38200         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38201             return nan ? '' : value;
38202         }
38203         return parseFloat(value).toFixed(this.decimalPrecision);
38204     },
38205
38206     setValue : function(v){
38207         v = this.fixPrecision(v);
38208         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38209     },
38210
38211     // private
38212     decimalPrecisionFcn : function(v){
38213         return Math.floor(v);
38214     },
38215
38216     beforeBlur : function(){
38217         var v = this.parseValue(this.getRawValue());
38218         if(v){
38219             this.setValue(v);
38220         }
38221     }
38222 });/*
38223  * Based on:
38224  * Ext JS Library 1.1.1
38225  * Copyright(c) 2006-2007, Ext JS, LLC.
38226  *
38227  * Originally Released Under LGPL - original licence link has changed is not relivant.
38228  *
38229  * Fork - LGPL
38230  * <script type="text/javascript">
38231  */
38232  
38233 /**
38234  * @class Roo.form.DateField
38235  * @extends Roo.form.TriggerField
38236  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38237 * @constructor
38238 * Create a new DateField
38239 * @param {Object} config
38240  */
38241 Roo.form.DateField = function(config){
38242     Roo.form.DateField.superclass.constructor.call(this, config);
38243     
38244       this.addEvents({
38245          
38246         /**
38247          * @event select
38248          * Fires when a date is selected
38249              * @param {Roo.form.DateField} combo This combo box
38250              * @param {Date} date The date selected
38251              */
38252         'select' : true
38253          
38254     });
38255     
38256     
38257     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38258     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38259     this.ddMatch = null;
38260     if(this.disabledDates){
38261         var dd = this.disabledDates;
38262         var re = "(?:";
38263         for(var i = 0; i < dd.length; i++){
38264             re += dd[i];
38265             if(i != dd.length-1) re += "|";
38266         }
38267         this.ddMatch = new RegExp(re + ")");
38268     }
38269 };
38270
38271 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38272     /**
38273      * @cfg {String} format
38274      * The default date format string which can be overriden for localization support.  The format must be
38275      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38276      */
38277     format : "m/d/y",
38278     /**
38279      * @cfg {String} altFormats
38280      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38281      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38282      */
38283     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38284     /**
38285      * @cfg {Array} disabledDays
38286      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38287      */
38288     disabledDays : null,
38289     /**
38290      * @cfg {String} disabledDaysText
38291      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38292      */
38293     disabledDaysText : "Disabled",
38294     /**
38295      * @cfg {Array} disabledDates
38296      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38297      * expression so they are very powerful. Some examples:
38298      * <ul>
38299      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38300      * <li>["03/08", "09/16"] would disable those days for every year</li>
38301      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38302      * <li>["03/../2006"] would disable every day in March 2006</li>
38303      * <li>["^03"] would disable every day in every March</li>
38304      * </ul>
38305      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38306      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38307      */
38308     disabledDates : null,
38309     /**
38310      * @cfg {String} disabledDatesText
38311      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38312      */
38313     disabledDatesText : "Disabled",
38314     /**
38315      * @cfg {Date/String} minValue
38316      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38317      * valid format (defaults to null).
38318      */
38319     minValue : null,
38320     /**
38321      * @cfg {Date/String} maxValue
38322      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38323      * valid format (defaults to null).
38324      */
38325     maxValue : null,
38326     /**
38327      * @cfg {String} minText
38328      * The error text to display when the date in the cell is before minValue (defaults to
38329      * 'The date in this field must be after {minValue}').
38330      */
38331     minText : "The date in this field must be equal to or after {0}",
38332     /**
38333      * @cfg {String} maxText
38334      * The error text to display when the date in the cell is after maxValue (defaults to
38335      * 'The date in this field must be before {maxValue}').
38336      */
38337     maxText : "The date in this field must be equal to or before {0}",
38338     /**
38339      * @cfg {String} invalidText
38340      * The error text to display when the date in the field is invalid (defaults to
38341      * '{value} is not a valid date - it must be in the format {format}').
38342      */
38343     invalidText : "{0} is not a valid date - it must be in the format {1}",
38344     /**
38345      * @cfg {String} triggerClass
38346      * An additional CSS class used to style the trigger button.  The trigger will always get the
38347      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38348      * which displays a calendar icon).
38349      */
38350     triggerClass : 'x-form-date-trigger',
38351     
38352
38353     /**
38354      * @cfg {Boolean} useIso
38355      * if enabled, then the date field will use a hidden field to store the 
38356      * real value as iso formated date. default (false)
38357      */ 
38358     useIso : false,
38359     /**
38360      * @cfg {String/Object} autoCreate
38361      * A DomHelper element spec, or true for a default element spec (defaults to
38362      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38363      */ 
38364     // private
38365     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38366     
38367     // private
38368     hiddenField: false,
38369     
38370     onRender : function(ct, position)
38371     {
38372         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38373         if (this.useIso) {
38374             //this.el.dom.removeAttribute('name'); 
38375             Roo.log("Changing name?");
38376             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38377             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38378                     'before', true);
38379             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38380             // prevent input submission
38381             this.hiddenName = this.name;
38382         }
38383             
38384             
38385     },
38386     
38387     // private
38388     validateValue : function(value)
38389     {
38390         value = this.formatDate(value);
38391         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38392             Roo.log('super failed');
38393             return false;
38394         }
38395         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38396              return true;
38397         }
38398         var svalue = value;
38399         value = this.parseDate(value);
38400         if(!value){
38401             Roo.log('parse date failed' + svalue);
38402             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38403             return false;
38404         }
38405         var time = value.getTime();
38406         if(this.minValue && time < this.minValue.getTime()){
38407             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38408             return false;
38409         }
38410         if(this.maxValue && time > this.maxValue.getTime()){
38411             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38412             return false;
38413         }
38414         if(this.disabledDays){
38415             var day = value.getDay();
38416             for(var i = 0; i < this.disabledDays.length; i++) {
38417                 if(day === this.disabledDays[i]){
38418                     this.markInvalid(this.disabledDaysText);
38419                     return false;
38420                 }
38421             }
38422         }
38423         var fvalue = this.formatDate(value);
38424         if(this.ddMatch && this.ddMatch.test(fvalue)){
38425             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38426             return false;
38427         }
38428         return true;
38429     },
38430
38431     // private
38432     // Provides logic to override the default TriggerField.validateBlur which just returns true
38433     validateBlur : function(){
38434         return !this.menu || !this.menu.isVisible();
38435     },
38436     
38437     getName: function()
38438     {
38439         // returns hidden if it's set..
38440         if (!this.rendered) {return ''};
38441         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38442         
38443     },
38444
38445     /**
38446      * Returns the current date value of the date field.
38447      * @return {Date} The date value
38448      */
38449     getValue : function(){
38450         
38451         return  this.hiddenField ?
38452                 this.hiddenField.value :
38453                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38454     },
38455
38456     /**
38457      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38458      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38459      * (the default format used is "m/d/y").
38460      * <br />Usage:
38461      * <pre><code>
38462 //All of these calls set the same date value (May 4, 2006)
38463
38464 //Pass a date object:
38465 var dt = new Date('5/4/06');
38466 dateField.setValue(dt);
38467
38468 //Pass a date string (default format):
38469 dateField.setValue('5/4/06');
38470
38471 //Pass a date string (custom format):
38472 dateField.format = 'Y-m-d';
38473 dateField.setValue('2006-5-4');
38474 </code></pre>
38475      * @param {String/Date} date The date or valid date string
38476      */
38477     setValue : function(date){
38478         if (this.hiddenField) {
38479             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38480         }
38481         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38482         // make sure the value field is always stored as a date..
38483         this.value = this.parseDate(date);
38484         
38485         
38486     },
38487
38488     // private
38489     parseDate : function(value){
38490         if(!value || value instanceof Date){
38491             return value;
38492         }
38493         var v = Date.parseDate(value, this.format);
38494          if (!v && this.useIso) {
38495             v = Date.parseDate(value, 'Y-m-d');
38496         }
38497         if(!v && this.altFormats){
38498             if(!this.altFormatsArray){
38499                 this.altFormatsArray = this.altFormats.split("|");
38500             }
38501             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38502                 v = Date.parseDate(value, this.altFormatsArray[i]);
38503             }
38504         }
38505         return v;
38506     },
38507
38508     // private
38509     formatDate : function(date, fmt){
38510         return (!date || !(date instanceof Date)) ?
38511                date : date.dateFormat(fmt || this.format);
38512     },
38513
38514     // private
38515     menuListeners : {
38516         select: function(m, d){
38517             
38518             this.setValue(d);
38519             this.fireEvent('select', this, d);
38520         },
38521         show : function(){ // retain focus styling
38522             this.onFocus();
38523         },
38524         hide : function(){
38525             this.focus.defer(10, this);
38526             var ml = this.menuListeners;
38527             this.menu.un("select", ml.select,  this);
38528             this.menu.un("show", ml.show,  this);
38529             this.menu.un("hide", ml.hide,  this);
38530         }
38531     },
38532
38533     // private
38534     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38535     onTriggerClick : function(){
38536         if(this.disabled){
38537             return;
38538         }
38539         if(this.menu == null){
38540             this.menu = new Roo.menu.DateMenu();
38541         }
38542         Roo.apply(this.menu.picker,  {
38543             showClear: this.allowBlank,
38544             minDate : this.minValue,
38545             maxDate : this.maxValue,
38546             disabledDatesRE : this.ddMatch,
38547             disabledDatesText : this.disabledDatesText,
38548             disabledDays : this.disabledDays,
38549             disabledDaysText : this.disabledDaysText,
38550             format : this.useIso ? 'Y-m-d' : this.format,
38551             minText : String.format(this.minText, this.formatDate(this.minValue)),
38552             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38553         });
38554         this.menu.on(Roo.apply({}, this.menuListeners, {
38555             scope:this
38556         }));
38557         this.menu.picker.setValue(this.getValue() || new Date());
38558         this.menu.show(this.el, "tl-bl?");
38559     },
38560
38561     beforeBlur : function(){
38562         var v = this.parseDate(this.getRawValue());
38563         if(v){
38564             this.setValue(v);
38565         }
38566     }
38567
38568     /** @cfg {Boolean} grow @hide */
38569     /** @cfg {Number} growMin @hide */
38570     /** @cfg {Number} growMax @hide */
38571     /**
38572      * @hide
38573      * @method autoSize
38574      */
38575 });/*
38576  * Based on:
38577  * Ext JS Library 1.1.1
38578  * Copyright(c) 2006-2007, Ext JS, LLC.
38579  *
38580  * Originally Released Under LGPL - original licence link has changed is not relivant.
38581  *
38582  * Fork - LGPL
38583  * <script type="text/javascript">
38584  */
38585  
38586 /**
38587  * @class Roo.form.MonthField
38588  * @extends Roo.form.TriggerField
38589  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38590 * @constructor
38591 * Create a new MonthField
38592 * @param {Object} config
38593  */
38594 Roo.form.MonthField = function(config){
38595     
38596     Roo.form.MonthField.superclass.constructor.call(this, config);
38597     
38598       this.addEvents({
38599          
38600         /**
38601          * @event select
38602          * Fires when a date is selected
38603              * @param {Roo.form.MonthFieeld} combo This combo box
38604              * @param {Date} date The date selected
38605              */
38606         'select' : true
38607          
38608     });
38609     
38610     
38611     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38612     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38613     this.ddMatch = null;
38614     if(this.disabledDates){
38615         var dd = this.disabledDates;
38616         var re = "(?:";
38617         for(var i = 0; i < dd.length; i++){
38618             re += dd[i];
38619             if(i != dd.length-1) re += "|";
38620         }
38621         this.ddMatch = new RegExp(re + ")");
38622     }
38623 };
38624
38625 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38626     /**
38627      * @cfg {String} format
38628      * The default date format string which can be overriden for localization support.  The format must be
38629      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38630      */
38631     format : "M Y",
38632     /**
38633      * @cfg {String} altFormats
38634      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38635      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38636      */
38637     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38638     /**
38639      * @cfg {Array} disabledDays
38640      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38641      */
38642     disabledDays : [0,1,2,3,4,5,6],
38643     /**
38644      * @cfg {String} disabledDaysText
38645      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38646      */
38647     disabledDaysText : "Disabled",
38648     /**
38649      * @cfg {Array} disabledDates
38650      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38651      * expression so they are very powerful. Some examples:
38652      * <ul>
38653      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38654      * <li>["03/08", "09/16"] would disable those days for every year</li>
38655      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38656      * <li>["03/../2006"] would disable every day in March 2006</li>
38657      * <li>["^03"] would disable every day in every March</li>
38658      * </ul>
38659      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38660      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38661      */
38662     disabledDates : null,
38663     /**
38664      * @cfg {String} disabledDatesText
38665      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38666      */
38667     disabledDatesText : "Disabled",
38668     /**
38669      * @cfg {Date/String} minValue
38670      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38671      * valid format (defaults to null).
38672      */
38673     minValue : null,
38674     /**
38675      * @cfg {Date/String} maxValue
38676      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38677      * valid format (defaults to null).
38678      */
38679     maxValue : null,
38680     /**
38681      * @cfg {String} minText
38682      * The error text to display when the date in the cell is before minValue (defaults to
38683      * 'The date in this field must be after {minValue}').
38684      */
38685     minText : "The date in this field must be equal to or after {0}",
38686     /**
38687      * @cfg {String} maxTextf
38688      * The error text to display when the date in the cell is after maxValue (defaults to
38689      * 'The date in this field must be before {maxValue}').
38690      */
38691     maxText : "The date in this field must be equal to or before {0}",
38692     /**
38693      * @cfg {String} invalidText
38694      * The error text to display when the date in the field is invalid (defaults to
38695      * '{value} is not a valid date - it must be in the format {format}').
38696      */
38697     invalidText : "{0} is not a valid date - it must be in the format {1}",
38698     /**
38699      * @cfg {String} triggerClass
38700      * An additional CSS class used to style the trigger button.  The trigger will always get the
38701      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38702      * which displays a calendar icon).
38703      */
38704     triggerClass : 'x-form-date-trigger',
38705     
38706
38707     /**
38708      * @cfg {Boolean} useIso
38709      * if enabled, then the date field will use a hidden field to store the 
38710      * real value as iso formated date. default (true)
38711      */ 
38712     useIso : true,
38713     /**
38714      * @cfg {String/Object} autoCreate
38715      * A DomHelper element spec, or true for a default element spec (defaults to
38716      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38717      */ 
38718     // private
38719     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38720     
38721     // private
38722     hiddenField: false,
38723     
38724     hideMonthPicker : false,
38725     
38726     onRender : function(ct, position)
38727     {
38728         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38729         if (this.useIso) {
38730             this.el.dom.removeAttribute('name'); 
38731             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38732                     'before', true);
38733             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38734             // prevent input submission
38735             this.hiddenName = this.name;
38736         }
38737             
38738             
38739     },
38740     
38741     // private
38742     validateValue : function(value)
38743     {
38744         value = this.formatDate(value);
38745         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38746             return false;
38747         }
38748         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38749              return true;
38750         }
38751         var svalue = value;
38752         value = this.parseDate(value);
38753         if(!value){
38754             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38755             return false;
38756         }
38757         var time = value.getTime();
38758         if(this.minValue && time < this.minValue.getTime()){
38759             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38760             return false;
38761         }
38762         if(this.maxValue && time > this.maxValue.getTime()){
38763             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38764             return false;
38765         }
38766         /*if(this.disabledDays){
38767             var day = value.getDay();
38768             for(var i = 0; i < this.disabledDays.length; i++) {
38769                 if(day === this.disabledDays[i]){
38770                     this.markInvalid(this.disabledDaysText);
38771                     return false;
38772                 }
38773             }
38774         }
38775         */
38776         var fvalue = this.formatDate(value);
38777         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38778             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38779             return false;
38780         }
38781         */
38782         return true;
38783     },
38784
38785     // private
38786     // Provides logic to override the default TriggerField.validateBlur which just returns true
38787     validateBlur : function(){
38788         return !this.menu || !this.menu.isVisible();
38789     },
38790
38791     /**
38792      * Returns the current date value of the date field.
38793      * @return {Date} The date value
38794      */
38795     getValue : function(){
38796         
38797         
38798         
38799         return  this.hiddenField ?
38800                 this.hiddenField.value :
38801                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38802     },
38803
38804     /**
38805      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38806      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38807      * (the default format used is "m/d/y").
38808      * <br />Usage:
38809      * <pre><code>
38810 //All of these calls set the same date value (May 4, 2006)
38811
38812 //Pass a date object:
38813 var dt = new Date('5/4/06');
38814 monthField.setValue(dt);
38815
38816 //Pass a date string (default format):
38817 monthField.setValue('5/4/06');
38818
38819 //Pass a date string (custom format):
38820 monthField.format = 'Y-m-d';
38821 monthField.setValue('2006-5-4');
38822 </code></pre>
38823      * @param {String/Date} date The date or valid date string
38824      */
38825     setValue : function(date){
38826         Roo.log('month setValue' + date);
38827         // can only be first of month..
38828         
38829         var val = this.parseDate(date);
38830         
38831         if (this.hiddenField) {
38832             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38833         }
38834         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38835         this.value = this.parseDate(date);
38836     },
38837
38838     // private
38839     parseDate : function(value){
38840         if(!value || value instanceof Date){
38841             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38842             return value;
38843         }
38844         var v = Date.parseDate(value, this.format);
38845         if (!v && this.useIso) {
38846             v = Date.parseDate(value, 'Y-m-d');
38847         }
38848         if (v) {
38849             // 
38850             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38851         }
38852         
38853         
38854         if(!v && this.altFormats){
38855             if(!this.altFormatsArray){
38856                 this.altFormatsArray = this.altFormats.split("|");
38857             }
38858             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38859                 v = Date.parseDate(value, this.altFormatsArray[i]);
38860             }
38861         }
38862         return v;
38863     },
38864
38865     // private
38866     formatDate : function(date, fmt){
38867         return (!date || !(date instanceof Date)) ?
38868                date : date.dateFormat(fmt || this.format);
38869     },
38870
38871     // private
38872     menuListeners : {
38873         select: function(m, d){
38874             this.setValue(d);
38875             this.fireEvent('select', this, d);
38876         },
38877         show : function(){ // retain focus styling
38878             this.onFocus();
38879         },
38880         hide : function(){
38881             this.focus.defer(10, this);
38882             var ml = this.menuListeners;
38883             this.menu.un("select", ml.select,  this);
38884             this.menu.un("show", ml.show,  this);
38885             this.menu.un("hide", ml.hide,  this);
38886         }
38887     },
38888     // private
38889     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38890     onTriggerClick : function(){
38891         if(this.disabled){
38892             return;
38893         }
38894         if(this.menu == null){
38895             this.menu = new Roo.menu.DateMenu();
38896            
38897         }
38898         
38899         Roo.apply(this.menu.picker,  {
38900             
38901             showClear: this.allowBlank,
38902             minDate : this.minValue,
38903             maxDate : this.maxValue,
38904             disabledDatesRE : this.ddMatch,
38905             disabledDatesText : this.disabledDatesText,
38906             
38907             format : this.useIso ? 'Y-m-d' : this.format,
38908             minText : String.format(this.minText, this.formatDate(this.minValue)),
38909             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38910             
38911         });
38912          this.menu.on(Roo.apply({}, this.menuListeners, {
38913             scope:this
38914         }));
38915        
38916         
38917         var m = this.menu;
38918         var p = m.picker;
38919         
38920         // hide month picker get's called when we called by 'before hide';
38921         
38922         var ignorehide = true;
38923         p.hideMonthPicker  = function(disableAnim){
38924             if (ignorehide) {
38925                 return;
38926             }
38927              if(this.monthPicker){
38928                 Roo.log("hideMonthPicker called");
38929                 if(disableAnim === true){
38930                     this.monthPicker.hide();
38931                 }else{
38932                     this.monthPicker.slideOut('t', {duration:.2});
38933                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38934                     p.fireEvent("select", this, this.value);
38935                     m.hide();
38936                 }
38937             }
38938         }
38939         
38940         Roo.log('picker set value');
38941         Roo.log(this.getValue());
38942         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38943         m.show(this.el, 'tl-bl?');
38944         ignorehide  = false;
38945         // this will trigger hideMonthPicker..
38946         
38947         
38948         // hidden the day picker
38949         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38950         
38951         
38952         
38953       
38954         
38955         p.showMonthPicker.defer(100, p);
38956     
38957         
38958        
38959     },
38960
38961     beforeBlur : function(){
38962         var v = this.parseDate(this.getRawValue());
38963         if(v){
38964             this.setValue(v);
38965         }
38966     }
38967
38968     /** @cfg {Boolean} grow @hide */
38969     /** @cfg {Number} growMin @hide */
38970     /** @cfg {Number} growMax @hide */
38971     /**
38972      * @hide
38973      * @method autoSize
38974      */
38975 });/*
38976  * Based on:
38977  * Ext JS Library 1.1.1
38978  * Copyright(c) 2006-2007, Ext JS, LLC.
38979  *
38980  * Originally Released Under LGPL - original licence link has changed is not relivant.
38981  *
38982  * Fork - LGPL
38983  * <script type="text/javascript">
38984  */
38985  
38986
38987 /**
38988  * @class Roo.form.ComboBox
38989  * @extends Roo.form.TriggerField
38990  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38991  * @constructor
38992  * Create a new ComboBox.
38993  * @param {Object} config Configuration options
38994  */
38995 Roo.form.ComboBox = function(config){
38996     Roo.form.ComboBox.superclass.constructor.call(this, config);
38997     this.addEvents({
38998         /**
38999          * @event expand
39000          * Fires when the dropdown list is expanded
39001              * @param {Roo.form.ComboBox} combo This combo box
39002              */
39003         'expand' : true,
39004         /**
39005          * @event collapse
39006          * Fires when the dropdown list is collapsed
39007              * @param {Roo.form.ComboBox} combo This combo box
39008              */
39009         'collapse' : true,
39010         /**
39011          * @event beforeselect
39012          * Fires before a list item is selected. Return false to cancel the selection.
39013              * @param {Roo.form.ComboBox} combo This combo box
39014              * @param {Roo.data.Record} record The data record returned from the underlying store
39015              * @param {Number} index The index of the selected item in the dropdown list
39016              */
39017         'beforeselect' : true,
39018         /**
39019          * @event select
39020          * Fires when a list item is selected
39021              * @param {Roo.form.ComboBox} combo This combo box
39022              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39023              * @param {Number} index The index of the selected item in the dropdown list
39024              */
39025         'select' : true,
39026         /**
39027          * @event beforequery
39028          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39029          * The event object passed has these properties:
39030              * @param {Roo.form.ComboBox} combo This combo box
39031              * @param {String} query The query
39032              * @param {Boolean} forceAll true to force "all" query
39033              * @param {Boolean} cancel true to cancel the query
39034              * @param {Object} e The query event object
39035              */
39036         'beforequery': true,
39037          /**
39038          * @event add
39039          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39040              * @param {Roo.form.ComboBox} combo This combo box
39041              */
39042         'add' : true,
39043         /**
39044          * @event edit
39045          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39046              * @param {Roo.form.ComboBox} combo This combo box
39047              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39048              */
39049         'edit' : true
39050         
39051         
39052     });
39053     if(this.transform){
39054         this.allowDomMove = false;
39055         var s = Roo.getDom(this.transform);
39056         if(!this.hiddenName){
39057             this.hiddenName = s.name;
39058         }
39059         if(!this.store){
39060             this.mode = 'local';
39061             var d = [], opts = s.options;
39062             for(var i = 0, len = opts.length;i < len; i++){
39063                 var o = opts[i];
39064                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39065                 if(o.selected) {
39066                     this.value = value;
39067                 }
39068                 d.push([value, o.text]);
39069             }
39070             this.store = new Roo.data.SimpleStore({
39071                 'id': 0,
39072                 fields: ['value', 'text'],
39073                 data : d
39074             });
39075             this.valueField = 'value';
39076             this.displayField = 'text';
39077         }
39078         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39079         if(!this.lazyRender){
39080             this.target = true;
39081             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39082             s.parentNode.removeChild(s); // remove it
39083             this.render(this.el.parentNode);
39084         }else{
39085             s.parentNode.removeChild(s); // remove it
39086         }
39087
39088     }
39089     if (this.store) {
39090         this.store = Roo.factory(this.store, Roo.data);
39091     }
39092     
39093     this.selectedIndex = -1;
39094     if(this.mode == 'local'){
39095         if(config.queryDelay === undefined){
39096             this.queryDelay = 10;
39097         }
39098         if(config.minChars === undefined){
39099             this.minChars = 0;
39100         }
39101     }
39102 };
39103
39104 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39105     /**
39106      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39107      */
39108     /**
39109      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39110      * rendering into an Roo.Editor, defaults to false)
39111      */
39112     /**
39113      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39114      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39115      */
39116     /**
39117      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39118      */
39119     /**
39120      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39121      * the dropdown list (defaults to undefined, with no header element)
39122      */
39123
39124      /**
39125      * @cfg {String/Roo.Template} tpl The template to use to render the output
39126      */
39127      
39128     // private
39129     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39130     /**
39131      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39132      */
39133     listWidth: undefined,
39134     /**
39135      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39136      * mode = 'remote' or 'text' if mode = 'local')
39137      */
39138     displayField: undefined,
39139     /**
39140      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39141      * mode = 'remote' or 'value' if mode = 'local'). 
39142      * Note: use of a valueField requires the user make a selection
39143      * in order for a value to be mapped.
39144      */
39145     valueField: undefined,
39146     
39147     
39148     /**
39149      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39150      * field's data value (defaults to the underlying DOM element's name)
39151      */
39152     hiddenName: undefined,
39153     /**
39154      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39155      */
39156     listClass: '',
39157     /**
39158      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39159      */
39160     selectedClass: 'x-combo-selected',
39161     /**
39162      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39163      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39164      * which displays a downward arrow icon).
39165      */
39166     triggerClass : 'x-form-arrow-trigger',
39167     /**
39168      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39169      */
39170     shadow:'sides',
39171     /**
39172      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39173      * anchor positions (defaults to 'tl-bl')
39174      */
39175     listAlign: 'tl-bl?',
39176     /**
39177      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39178      */
39179     maxHeight: 300,
39180     /**
39181      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39182      * query specified by the allQuery config option (defaults to 'query')
39183      */
39184     triggerAction: 'query',
39185     /**
39186      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39187      * (defaults to 4, does not apply if editable = false)
39188      */
39189     minChars : 4,
39190     /**
39191      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39192      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39193      */
39194     typeAhead: false,
39195     /**
39196      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39197      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39198      */
39199     queryDelay: 500,
39200     /**
39201      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39202      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39203      */
39204     pageSize: 0,
39205     /**
39206      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39207      * when editable = true (defaults to false)
39208      */
39209     selectOnFocus:false,
39210     /**
39211      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39212      */
39213     queryParam: 'query',
39214     /**
39215      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39216      * when mode = 'remote' (defaults to 'Loading...')
39217      */
39218     loadingText: 'Loading...',
39219     /**
39220      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39221      */
39222     resizable: false,
39223     /**
39224      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39225      */
39226     handleHeight : 8,
39227     /**
39228      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39229      * traditional select (defaults to true)
39230      */
39231     editable: true,
39232     /**
39233      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39234      */
39235     allQuery: '',
39236     /**
39237      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39238      */
39239     mode: 'remote',
39240     /**
39241      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39242      * listWidth has a higher value)
39243      */
39244     minListWidth : 70,
39245     /**
39246      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39247      * allow the user to set arbitrary text into the field (defaults to false)
39248      */
39249     forceSelection:false,
39250     /**
39251      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39252      * if typeAhead = true (defaults to 250)
39253      */
39254     typeAheadDelay : 250,
39255     /**
39256      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39257      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39258      */
39259     valueNotFoundText : undefined,
39260     /**
39261      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39262      */
39263     blockFocus : false,
39264     
39265     /**
39266      * @cfg {Boolean} disableClear Disable showing of clear button.
39267      */
39268     disableClear : false,
39269     /**
39270      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39271      */
39272     alwaysQuery : false,
39273     
39274     //private
39275     addicon : false,
39276     editicon: false,
39277     
39278     // element that contains real text value.. (when hidden is used..)
39279      
39280     // private
39281     onRender : function(ct, position){
39282         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39283         if(this.hiddenName){
39284             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39285                     'before', true);
39286             this.hiddenField.value =
39287                 this.hiddenValue !== undefined ? this.hiddenValue :
39288                 this.value !== undefined ? this.value : '';
39289
39290             // prevent input submission
39291             this.el.dom.removeAttribute('name');
39292              
39293              
39294         }
39295         if(Roo.isGecko){
39296             this.el.dom.setAttribute('autocomplete', 'off');
39297         }
39298
39299         var cls = 'x-combo-list';
39300
39301         this.list = new Roo.Layer({
39302             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39303         });
39304
39305         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39306         this.list.setWidth(lw);
39307         this.list.swallowEvent('mousewheel');
39308         this.assetHeight = 0;
39309
39310         if(this.title){
39311             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39312             this.assetHeight += this.header.getHeight();
39313         }
39314
39315         this.innerList = this.list.createChild({cls:cls+'-inner'});
39316         this.innerList.on('mouseover', this.onViewOver, this);
39317         this.innerList.on('mousemove', this.onViewMove, this);
39318         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39319         
39320         if(this.allowBlank && !this.pageSize && !this.disableClear){
39321             this.footer = this.list.createChild({cls:cls+'-ft'});
39322             this.pageTb = new Roo.Toolbar(this.footer);
39323            
39324         }
39325         if(this.pageSize){
39326             this.footer = this.list.createChild({cls:cls+'-ft'});
39327             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39328                     {pageSize: this.pageSize});
39329             
39330         }
39331         
39332         if (this.pageTb && this.allowBlank && !this.disableClear) {
39333             var _this = this;
39334             this.pageTb.add(new Roo.Toolbar.Fill(), {
39335                 cls: 'x-btn-icon x-btn-clear',
39336                 text: '&#160;',
39337                 handler: function()
39338                 {
39339                     _this.collapse();
39340                     _this.clearValue();
39341                     _this.onSelect(false, -1);
39342                 }
39343             });
39344         }
39345         if (this.footer) {
39346             this.assetHeight += this.footer.getHeight();
39347         }
39348         
39349
39350         if(!this.tpl){
39351             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39352         }
39353
39354         this.view = new Roo.View(this.innerList, this.tpl, {
39355             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39356         });
39357
39358         this.view.on('click', this.onViewClick, this);
39359
39360         this.store.on('beforeload', this.onBeforeLoad, this);
39361         this.store.on('load', this.onLoad, this);
39362         this.store.on('loadexception', this.onLoadException, this);
39363
39364         if(this.resizable){
39365             this.resizer = new Roo.Resizable(this.list,  {
39366                pinned:true, handles:'se'
39367             });
39368             this.resizer.on('resize', function(r, w, h){
39369                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39370                 this.listWidth = w;
39371                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39372                 this.restrictHeight();
39373             }, this);
39374             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39375         }
39376         if(!this.editable){
39377             this.editable = true;
39378             this.setEditable(false);
39379         }  
39380         
39381         
39382         if (typeof(this.events.add.listeners) != 'undefined') {
39383             
39384             this.addicon = this.wrap.createChild(
39385                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39386        
39387             this.addicon.on('click', function(e) {
39388                 this.fireEvent('add', this);
39389             }, this);
39390         }
39391         if (typeof(this.events.edit.listeners) != 'undefined') {
39392             
39393             this.editicon = this.wrap.createChild(
39394                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39395             if (this.addicon) {
39396                 this.editicon.setStyle('margin-left', '40px');
39397             }
39398             this.editicon.on('click', function(e) {
39399                 
39400                 // we fire even  if inothing is selected..
39401                 this.fireEvent('edit', this, this.lastData );
39402                 
39403             }, this);
39404         }
39405         
39406         
39407         
39408     },
39409
39410     // private
39411     initEvents : function(){
39412         Roo.form.ComboBox.superclass.initEvents.call(this);
39413
39414         this.keyNav = new Roo.KeyNav(this.el, {
39415             "up" : function(e){
39416                 this.inKeyMode = true;
39417                 this.selectPrev();
39418             },
39419
39420             "down" : function(e){
39421                 if(!this.isExpanded()){
39422                     this.onTriggerClick();
39423                 }else{
39424                     this.inKeyMode = true;
39425                     this.selectNext();
39426                 }
39427             },
39428
39429             "enter" : function(e){
39430                 this.onViewClick();
39431                 //return true;
39432             },
39433
39434             "esc" : function(e){
39435                 this.collapse();
39436             },
39437
39438             "tab" : function(e){
39439                 this.onViewClick(false);
39440                 this.fireEvent("specialkey", this, e);
39441                 return true;
39442             },
39443
39444             scope : this,
39445
39446             doRelay : function(foo, bar, hname){
39447                 if(hname == 'down' || this.scope.isExpanded()){
39448                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39449                 }
39450                 return true;
39451             },
39452
39453             forceKeyDown: true
39454         });
39455         this.queryDelay = Math.max(this.queryDelay || 10,
39456                 this.mode == 'local' ? 10 : 250);
39457         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39458         if(this.typeAhead){
39459             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39460         }
39461         if(this.editable !== false){
39462             this.el.on("keyup", this.onKeyUp, this);
39463         }
39464         if(this.forceSelection){
39465             this.on('blur', this.doForce, this);
39466         }
39467     },
39468
39469     onDestroy : function(){
39470         if(this.view){
39471             this.view.setStore(null);
39472             this.view.el.removeAllListeners();
39473             this.view.el.remove();
39474             this.view.purgeListeners();
39475         }
39476         if(this.list){
39477             this.list.destroy();
39478         }
39479         if(this.store){
39480             this.store.un('beforeload', this.onBeforeLoad, this);
39481             this.store.un('load', this.onLoad, this);
39482             this.store.un('loadexception', this.onLoadException, this);
39483         }
39484         Roo.form.ComboBox.superclass.onDestroy.call(this);
39485     },
39486
39487     // private
39488     fireKey : function(e){
39489         if(e.isNavKeyPress() && !this.list.isVisible()){
39490             this.fireEvent("specialkey", this, e);
39491         }
39492     },
39493
39494     // private
39495     onResize: function(w, h){
39496         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39497         
39498         if(typeof w != 'number'){
39499             // we do not handle it!?!?
39500             return;
39501         }
39502         var tw = this.trigger.getWidth();
39503         tw += this.addicon ? this.addicon.getWidth() : 0;
39504         tw += this.editicon ? this.editicon.getWidth() : 0;
39505         var x = w - tw;
39506         this.el.setWidth( this.adjustWidth('input', x));
39507             
39508         this.trigger.setStyle('left', x+'px');
39509         
39510         if(this.list && this.listWidth === undefined){
39511             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39512             this.list.setWidth(lw);
39513             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39514         }
39515         
39516     
39517         
39518     },
39519
39520     /**
39521      * Allow or prevent the user from directly editing the field text.  If false is passed,
39522      * the user will only be able to select from the items defined in the dropdown list.  This method
39523      * is the runtime equivalent of setting the 'editable' config option at config time.
39524      * @param {Boolean} value True to allow the user to directly edit the field text
39525      */
39526     setEditable : function(value){
39527         if(value == this.editable){
39528             return;
39529         }
39530         this.editable = value;
39531         if(!value){
39532             this.el.dom.setAttribute('readOnly', true);
39533             this.el.on('mousedown', this.onTriggerClick,  this);
39534             this.el.addClass('x-combo-noedit');
39535         }else{
39536             this.el.dom.setAttribute('readOnly', false);
39537             this.el.un('mousedown', this.onTriggerClick,  this);
39538             this.el.removeClass('x-combo-noedit');
39539         }
39540     },
39541
39542     // private
39543     onBeforeLoad : function(){
39544         if(!this.hasFocus){
39545             return;
39546         }
39547         this.innerList.update(this.loadingText ?
39548                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39549         this.restrictHeight();
39550         this.selectedIndex = -1;
39551     },
39552
39553     // private
39554     onLoad : function(){
39555         if(!this.hasFocus){
39556             return;
39557         }
39558         if(this.store.getCount() > 0){
39559             this.expand();
39560             this.restrictHeight();
39561             if(this.lastQuery == this.allQuery){
39562                 if(this.editable){
39563                     this.el.dom.select();
39564                 }
39565                 if(!this.selectByValue(this.value, true)){
39566                     this.select(0, true);
39567                 }
39568             }else{
39569                 this.selectNext();
39570                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39571                     this.taTask.delay(this.typeAheadDelay);
39572                 }
39573             }
39574         }else{
39575             this.onEmptyResults();
39576         }
39577         //this.el.focus();
39578     },
39579     // private
39580     onLoadException : function()
39581     {
39582         this.collapse();
39583         Roo.log(this.store.reader.jsonData);
39584         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39585             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39586         }
39587         
39588         
39589     },
39590     // private
39591     onTypeAhead : function(){
39592         if(this.store.getCount() > 0){
39593             var r = this.store.getAt(0);
39594             var newValue = r.data[this.displayField];
39595             var len = newValue.length;
39596             var selStart = this.getRawValue().length;
39597             if(selStart != len){
39598                 this.setRawValue(newValue);
39599                 this.selectText(selStart, newValue.length);
39600             }
39601         }
39602     },
39603
39604     // private
39605     onSelect : function(record, index){
39606         if(this.fireEvent('beforeselect', this, record, index) !== false){
39607             this.setFromData(index > -1 ? record.data : false);
39608             this.collapse();
39609             this.fireEvent('select', this, record, index);
39610         }
39611     },
39612
39613     /**
39614      * Returns the currently selected field value or empty string if no value is set.
39615      * @return {String} value The selected value
39616      */
39617     getValue : function(){
39618         if(this.valueField){
39619             return typeof this.value != 'undefined' ? this.value : '';
39620         }else{
39621             return Roo.form.ComboBox.superclass.getValue.call(this);
39622         }
39623     },
39624
39625     /**
39626      * Clears any text/value currently set in the field
39627      */
39628     clearValue : function(){
39629         if(this.hiddenField){
39630             this.hiddenField.value = '';
39631         }
39632         this.value = '';
39633         this.setRawValue('');
39634         this.lastSelectionText = '';
39635         
39636     },
39637
39638     /**
39639      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39640      * will be displayed in the field.  If the value does not match the data value of an existing item,
39641      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39642      * Otherwise the field will be blank (although the value will still be set).
39643      * @param {String} value The value to match
39644      */
39645     setValue : function(v){
39646         var text = v;
39647         if(this.valueField){
39648             var r = this.findRecord(this.valueField, v);
39649             if(r){
39650                 text = r.data[this.displayField];
39651             }else if(this.valueNotFoundText !== undefined){
39652                 text = this.valueNotFoundText;
39653             }
39654         }
39655         this.lastSelectionText = text;
39656         if(this.hiddenField){
39657             this.hiddenField.value = v;
39658         }
39659         Roo.form.ComboBox.superclass.setValue.call(this, text);
39660         this.value = v;
39661     },
39662     /**
39663      * @property {Object} the last set data for the element
39664      */
39665     
39666     lastData : false,
39667     /**
39668      * Sets the value of the field based on a object which is related to the record format for the store.
39669      * @param {Object} value the value to set as. or false on reset?
39670      */
39671     setFromData : function(o){
39672         var dv = ''; // display value
39673         var vv = ''; // value value..
39674         this.lastData = o;
39675         if (this.displayField) {
39676             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39677         } else {
39678             // this is an error condition!!!
39679             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39680         }
39681         
39682         if(this.valueField){
39683             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39684         }
39685         if(this.hiddenField){
39686             this.hiddenField.value = vv;
39687             
39688             this.lastSelectionText = dv;
39689             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39690             this.value = vv;
39691             return;
39692         }
39693         // no hidden field.. - we store the value in 'value', but still display
39694         // display field!!!!
39695         this.lastSelectionText = dv;
39696         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39697         this.value = vv;
39698         
39699         
39700     },
39701     // private
39702     reset : function(){
39703         // overridden so that last data is reset..
39704         this.setValue(this.originalValue);
39705         this.clearInvalid();
39706         this.lastData = false;
39707         if (this.view) {
39708             this.view.clearSelections();
39709         }
39710     },
39711     // private
39712     findRecord : function(prop, value){
39713         var record;
39714         if(this.store.getCount() > 0){
39715             this.store.each(function(r){
39716                 if(r.data[prop] == value){
39717                     record = r;
39718                     return false;
39719                 }
39720                 return true;
39721             });
39722         }
39723         return record;
39724     },
39725     
39726     getName: function()
39727     {
39728         // returns hidden if it's set..
39729         if (!this.rendered) {return ''};
39730         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39731         
39732     },
39733     // private
39734     onViewMove : function(e, t){
39735         this.inKeyMode = false;
39736     },
39737
39738     // private
39739     onViewOver : function(e, t){
39740         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39741             return;
39742         }
39743         var item = this.view.findItemFromChild(t);
39744         if(item){
39745             var index = this.view.indexOf(item);
39746             this.select(index, false);
39747         }
39748     },
39749
39750     // private
39751     onViewClick : function(doFocus)
39752     {
39753         var index = this.view.getSelectedIndexes()[0];
39754         var r = this.store.getAt(index);
39755         if(r){
39756             this.onSelect(r, index);
39757         }
39758         if(doFocus !== false && !this.blockFocus){
39759             this.el.focus();
39760         }
39761     },
39762
39763     // private
39764     restrictHeight : function(){
39765         this.innerList.dom.style.height = '';
39766         var inner = this.innerList.dom;
39767         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39768         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39769         this.list.beginUpdate();
39770         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39771         this.list.alignTo(this.el, this.listAlign);
39772         this.list.endUpdate();
39773     },
39774
39775     // private
39776     onEmptyResults : function(){
39777         this.collapse();
39778     },
39779
39780     /**
39781      * Returns true if the dropdown list is expanded, else false.
39782      */
39783     isExpanded : function(){
39784         return this.list.isVisible();
39785     },
39786
39787     /**
39788      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39789      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39790      * @param {String} value The data value of the item to select
39791      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39792      * selected item if it is not currently in view (defaults to true)
39793      * @return {Boolean} True if the value matched an item in the list, else false
39794      */
39795     selectByValue : function(v, scrollIntoView){
39796         if(v !== undefined && v !== null){
39797             var r = this.findRecord(this.valueField || this.displayField, v);
39798             if(r){
39799                 this.select(this.store.indexOf(r), scrollIntoView);
39800                 return true;
39801             }
39802         }
39803         return false;
39804     },
39805
39806     /**
39807      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39808      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39809      * @param {Number} index The zero-based index of the list item to select
39810      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39811      * selected item if it is not currently in view (defaults to true)
39812      */
39813     select : function(index, scrollIntoView){
39814         this.selectedIndex = index;
39815         this.view.select(index);
39816         if(scrollIntoView !== false){
39817             var el = this.view.getNode(index);
39818             if(el){
39819                 this.innerList.scrollChildIntoView(el, false);
39820             }
39821         }
39822     },
39823
39824     // private
39825     selectNext : function(){
39826         var ct = this.store.getCount();
39827         if(ct > 0){
39828             if(this.selectedIndex == -1){
39829                 this.select(0);
39830             }else if(this.selectedIndex < ct-1){
39831                 this.select(this.selectedIndex+1);
39832             }
39833         }
39834     },
39835
39836     // private
39837     selectPrev : function(){
39838         var ct = this.store.getCount();
39839         if(ct > 0){
39840             if(this.selectedIndex == -1){
39841                 this.select(0);
39842             }else if(this.selectedIndex != 0){
39843                 this.select(this.selectedIndex-1);
39844             }
39845         }
39846     },
39847
39848     // private
39849     onKeyUp : function(e){
39850         if(this.editable !== false && !e.isSpecialKey()){
39851             this.lastKey = e.getKey();
39852             this.dqTask.delay(this.queryDelay);
39853         }
39854     },
39855
39856     // private
39857     validateBlur : function(){
39858         return !this.list || !this.list.isVisible();   
39859     },
39860
39861     // private
39862     initQuery : function(){
39863         this.doQuery(this.getRawValue());
39864     },
39865
39866     // private
39867     doForce : function(){
39868         if(this.el.dom.value.length > 0){
39869             this.el.dom.value =
39870                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39871              
39872         }
39873     },
39874
39875     /**
39876      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39877      * query allowing the query action to be canceled if needed.
39878      * @param {String} query The SQL query to execute
39879      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39880      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39881      * saved in the current store (defaults to false)
39882      */
39883     doQuery : function(q, forceAll){
39884         if(q === undefined || q === null){
39885             q = '';
39886         }
39887         var qe = {
39888             query: q,
39889             forceAll: forceAll,
39890             combo: this,
39891             cancel:false
39892         };
39893         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39894             return false;
39895         }
39896         q = qe.query;
39897         forceAll = qe.forceAll;
39898         if(forceAll === true || (q.length >= this.minChars)){
39899             if(this.lastQuery != q || this.alwaysQuery){
39900                 this.lastQuery = q;
39901                 if(this.mode == 'local'){
39902                     this.selectedIndex = -1;
39903                     if(forceAll){
39904                         this.store.clearFilter();
39905                     }else{
39906                         this.store.filter(this.displayField, q);
39907                     }
39908                     this.onLoad();
39909                 }else{
39910                     this.store.baseParams[this.queryParam] = q;
39911                     this.store.load({
39912                         params: this.getParams(q)
39913                     });
39914                     this.expand();
39915                 }
39916             }else{
39917                 this.selectedIndex = -1;
39918                 this.onLoad();   
39919             }
39920         }
39921     },
39922
39923     // private
39924     getParams : function(q){
39925         var p = {};
39926         //p[this.queryParam] = q;
39927         if(this.pageSize){
39928             p.start = 0;
39929             p.limit = this.pageSize;
39930         }
39931         return p;
39932     },
39933
39934     /**
39935      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39936      */
39937     collapse : function(){
39938         if(!this.isExpanded()){
39939             return;
39940         }
39941         this.list.hide();
39942         Roo.get(document).un('mousedown', this.collapseIf, this);
39943         Roo.get(document).un('mousewheel', this.collapseIf, this);
39944         if (!this.editable) {
39945             Roo.get(document).un('keydown', this.listKeyPress, this);
39946         }
39947         this.fireEvent('collapse', this);
39948     },
39949
39950     // private
39951     collapseIf : function(e){
39952         if(!e.within(this.wrap) && !e.within(this.list)){
39953             this.collapse();
39954         }
39955     },
39956
39957     /**
39958      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39959      */
39960     expand : function(){
39961         if(this.isExpanded() || !this.hasFocus){
39962             return;
39963         }
39964         this.list.alignTo(this.el, this.listAlign);
39965         this.list.show();
39966         Roo.get(document).on('mousedown', this.collapseIf, this);
39967         Roo.get(document).on('mousewheel', this.collapseIf, this);
39968         if (!this.editable) {
39969             Roo.get(document).on('keydown', this.listKeyPress, this);
39970         }
39971         
39972         this.fireEvent('expand', this);
39973     },
39974
39975     // private
39976     // Implements the default empty TriggerField.onTriggerClick function
39977     onTriggerClick : function(){
39978         if(this.disabled){
39979             return;
39980         }
39981         if(this.isExpanded()){
39982             this.collapse();
39983             if (!this.blockFocus) {
39984                 this.el.focus();
39985             }
39986             
39987         }else {
39988             this.hasFocus = true;
39989             if(this.triggerAction == 'all') {
39990                 this.doQuery(this.allQuery, true);
39991             } else {
39992                 this.doQuery(this.getRawValue());
39993             }
39994             if (!this.blockFocus) {
39995                 this.el.focus();
39996             }
39997         }
39998     },
39999     listKeyPress : function(e)
40000     {
40001         //Roo.log('listkeypress');
40002         // scroll to first matching element based on key pres..
40003         if (e.isSpecialKey()) {
40004             return false;
40005         }
40006         var k = String.fromCharCode(e.getKey()).toUpperCase();
40007         //Roo.log(k);
40008         var match  = false;
40009         var csel = this.view.getSelectedNodes();
40010         var cselitem = false;
40011         if (csel.length) {
40012             var ix = this.view.indexOf(csel[0]);
40013             cselitem  = this.store.getAt(ix);
40014             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40015                 cselitem = false;
40016             }
40017             
40018         }
40019         
40020         this.store.each(function(v) { 
40021             if (cselitem) {
40022                 // start at existing selection.
40023                 if (cselitem.id == v.id) {
40024                     cselitem = false;
40025                 }
40026                 return;
40027             }
40028                 
40029             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40030                 match = this.store.indexOf(v);
40031                 return false;
40032             }
40033         }, this);
40034         
40035         if (match === false) {
40036             return true; // no more action?
40037         }
40038         // scroll to?
40039         this.view.select(match);
40040         var sn = Roo.get(this.view.getSelectedNodes()[0])
40041         sn.scrollIntoView(sn.dom.parentNode, false);
40042     }
40043
40044     /** 
40045     * @cfg {Boolean} grow 
40046     * @hide 
40047     */
40048     /** 
40049     * @cfg {Number} growMin 
40050     * @hide 
40051     */
40052     /** 
40053     * @cfg {Number} growMax 
40054     * @hide 
40055     */
40056     /**
40057      * @hide
40058      * @method autoSize
40059      */
40060 });/*
40061  * Copyright(c) 2010-2012, Roo J Solutions Limited
40062  *
40063  * Licence LGPL
40064  *
40065  */
40066
40067 /**
40068  * @class Roo.form.ComboBoxArray
40069  * @extends Roo.form.TextField
40070  * A facebook style adder... for lists of email / people / countries  etc...
40071  * pick multiple items from a combo box, and shows each one.
40072  *
40073  *  Fred [x]  Brian [x]  [Pick another |v]
40074  *
40075  *
40076  *  For this to work: it needs various extra information
40077  *    - normal combo problay has
40078  *      name, hiddenName
40079  *    + displayField, valueField
40080  *
40081  *    For our purpose...
40082  *
40083  *
40084  *   If we change from 'extends' to wrapping...
40085  *   
40086  *  
40087  *
40088  
40089  
40090  * @constructor
40091  * Create a new ComboBoxArray.
40092  * @param {Object} config Configuration options
40093  */
40094  
40095
40096 Roo.form.ComboBoxArray = function(config)
40097 {
40098     
40099     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40100     
40101     this.items = new Roo.util.MixedCollection(false);
40102     
40103     // construct the child combo...
40104     
40105     
40106     
40107     
40108    
40109     
40110 }
40111
40112  
40113 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40114
40115     /**
40116      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40117      */
40118     
40119     lastData : false,
40120     
40121     // behavies liek a hiddne field
40122     inputType:      'hidden',
40123     /**
40124      * @cfg {Number} width The width of the box that displays the selected element
40125      */ 
40126     width:          300,
40127
40128     
40129     
40130     /**
40131      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40132      */
40133     name : false,
40134     /**
40135      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40136      */
40137     hiddenName : false,
40138     
40139     
40140     // private the array of items that are displayed..
40141     items  : false,
40142     // private - the hidden field el.
40143     hiddenEl : false,
40144     // private - the filed el..
40145     el : false,
40146     
40147     //validateValue : function() { return true; }, // all values are ok!
40148     //onAddClick: function() { },
40149     
40150     onRender : function(ct, position) 
40151     {
40152         
40153         // create the standard hidden element
40154         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40155         
40156         
40157         // give fake names to child combo;
40158         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40159         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40160         
40161         this.combo = Roo.factory(this.combo, Roo.form);
40162         this.combo.onRender(ct, position);
40163         if (typeof(this.combo.width) != 'undefined') {
40164             this.combo.onResize(this.combo.width,0);
40165         }
40166         
40167         this.combo.initEvents();
40168         
40169         // assigned so form know we need to do this..
40170         this.store          = this.combo.store;
40171         this.valueField     = this.combo.valueField;
40172         this.displayField   = this.combo.displayField ;
40173         
40174         
40175         this.combo.wrap.addClass('x-cbarray-grp');
40176         
40177         var cbwrap = this.combo.wrap.createChild(
40178             {tag: 'div', cls: 'x-cbarray-cb'},
40179             this.combo.el.dom
40180         );
40181         
40182              
40183         this.hiddenEl = this.combo.wrap.createChild({
40184             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40185         });
40186         this.el = this.combo.wrap.createChild({
40187             tag: 'input',  type:'hidden' , name: this.name, value : ''
40188         });
40189          //   this.el.dom.removeAttribute("name");
40190         
40191         
40192         this.outerWrap = this.combo.wrap;
40193         this.wrap = cbwrap;
40194         
40195         this.outerWrap.setWidth(this.width);
40196         this.outerWrap.dom.removeChild(this.el.dom);
40197         
40198         this.wrap.dom.appendChild(this.el.dom);
40199         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40200         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40201         
40202         this.combo.trigger.setStyle('position','relative');
40203         this.combo.trigger.setStyle('left', '0px');
40204         this.combo.trigger.setStyle('top', '2px');
40205         
40206         this.combo.el.setStyle('vertical-align', 'text-bottom');
40207         
40208         //this.trigger.setStyle('vertical-align', 'top');
40209         
40210         // this should use the code from combo really... on('add' ....)
40211         if (this.adder) {
40212             
40213         
40214             this.adder = this.outerWrap.createChild(
40215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40216             var _t = this;
40217             this.adder.on('click', function(e) {
40218                 _t.fireEvent('adderclick', this, e);
40219             }, _t);
40220         }
40221         //var _t = this;
40222         //this.adder.on('click', this.onAddClick, _t);
40223         
40224         
40225         this.combo.on('select', function(cb, rec, ix) {
40226             this.addItem(rec.data);
40227             
40228             cb.setValue('');
40229             cb.el.dom.value = '';
40230             //cb.lastData = rec.data;
40231             // add to list
40232             
40233         }, this);
40234         
40235         
40236     },
40237     
40238     
40239     getName: function()
40240     {
40241         // returns hidden if it's set..
40242         if (!this.rendered) {return ''};
40243         return  this.hiddenName ? this.hiddenName : this.name;
40244         
40245     },
40246     
40247     
40248     onResize: function(w, h){
40249         
40250         return;
40251         // not sure if this is needed..
40252         //this.combo.onResize(w,h);
40253         
40254         if(typeof w != 'number'){
40255             // we do not handle it!?!?
40256             return;
40257         }
40258         var tw = this.combo.trigger.getWidth();
40259         tw += this.addicon ? this.addicon.getWidth() : 0;
40260         tw += this.editicon ? this.editicon.getWidth() : 0;
40261         var x = w - tw;
40262         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40263             
40264         this.combo.trigger.setStyle('left', '0px');
40265         
40266         if(this.list && this.listWidth === undefined){
40267             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40268             this.list.setWidth(lw);
40269             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40270         }
40271         
40272     
40273         
40274     },
40275     
40276     addItem: function(rec)
40277     {
40278         var valueField = this.combo.valueField;
40279         var displayField = this.combo.displayField;
40280         if (this.items.indexOfKey(rec[valueField]) > -1) {
40281             //console.log("GOT " + rec.data.id);
40282             return;
40283         }
40284         
40285         var x = new Roo.form.ComboBoxArray.Item({
40286             //id : rec[this.idField],
40287             data : rec,
40288             displayField : displayField ,
40289             tipField : displayField ,
40290             cb : this
40291         });
40292         // use the 
40293         this.items.add(rec[valueField],x);
40294         // add it before the element..
40295         this.updateHiddenEl();
40296         x.render(this.outerWrap, this.wrap.dom);
40297         // add the image handler..
40298     },
40299     
40300     updateHiddenEl : function()
40301     {
40302         this.validate();
40303         if (!this.hiddenEl) {
40304             return;
40305         }
40306         var ar = [];
40307         var idField = this.combo.valueField;
40308         
40309         this.items.each(function(f) {
40310             ar.push(f.data[idField]);
40311            
40312         });
40313         this.hiddenEl.dom.value = ar.join(',');
40314         this.validate();
40315     },
40316     
40317     reset : function()
40318     {
40319         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40320         this.items.each(function(f) {
40321            f.remove(); 
40322         });
40323         this.el.dom.value = '';
40324         if (this.hiddenEl) {
40325             this.hiddenEl.dom.value = '';
40326         }
40327         
40328     },
40329     getValue: function()
40330     {
40331         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40332     },
40333     setValue: function(v) // not a valid action - must use addItems..
40334     {
40335          
40336         this.reset();
40337         
40338         
40339         
40340         if (this.store.isLocal && (typeof(v) == 'string')) {
40341             // then we can use the store to find the values..
40342             // comma seperated at present.. this needs to allow JSON based encoding..
40343             this.hiddenEl.value  = v;
40344             var v_ar = [];
40345             Roo.each(v.split(','), function(k) {
40346                 Roo.log("CHECK " + this.valueField + ',' + k);
40347                 var li = this.store.query(this.valueField, k);
40348                 if (!li.length) {
40349                     return;
40350                 }
40351                 var add = {};
40352                 add[this.valueField] = k;
40353                 add[this.displayField] = li.item(0).data[this.displayField];
40354                 
40355                 this.addItem(add);
40356             }, this) 
40357              
40358         }
40359         if (typeof(v) == 'object') {
40360             // then let's assume it's an array of objects..
40361             Roo.each(v, function(l) {
40362                 this.addItem(l);
40363             }, this);
40364              
40365         }
40366         
40367         
40368     },
40369     setFromData: function(v)
40370     {
40371         // this recieves an object, if setValues is called.
40372         this.reset();
40373         this.el.dom.value = v[this.displayField];
40374         this.hiddenEl.dom.value = v[this.valueField];
40375         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40376             return;
40377         }
40378         var kv = v[this.valueField];
40379         var dv = v[this.displayField];
40380         kv = typeof(kv) != 'string' ? '' : kv;
40381         dv = typeof(dv) != 'string' ? '' : dv;
40382         
40383         
40384         var keys = kv.split(',');
40385         var display = dv.split(',');
40386         for (var i = 0 ; i < keys.length; i++) {
40387             
40388             add = {};
40389             add[this.valueField] = keys[i];
40390             add[this.displayField] = display[i];
40391             this.addItem(add);
40392         }
40393       
40394         
40395     },
40396     
40397     /**
40398      * Validates the combox array value
40399      * @return {Boolean} True if the value is valid, else false
40400      */
40401     validate : function(){
40402         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40403             this.clearInvalid();
40404             return true;
40405         }
40406         return false;
40407     },
40408     
40409     validateValue : function(value){
40410         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40411         
40412     }
40413     
40414 });
40415
40416
40417
40418 /**
40419  * @class Roo.form.ComboBoxArray.Item
40420  * @extends Roo.BoxComponent
40421  * A selected item in the list
40422  *  Fred [x]  Brian [x]  [Pick another |v]
40423  * 
40424  * @constructor
40425  * Create a new item.
40426  * @param {Object} config Configuration options
40427  */
40428  
40429 Roo.form.ComboBoxArray.Item = function(config) {
40430     config.id = Roo.id();
40431     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40432 }
40433
40434 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40435     data : {},
40436     cb: false,
40437     displayField : false,
40438     tipField : false,
40439     
40440     
40441     defaultAutoCreate : {
40442         tag: 'div',
40443         cls: 'x-cbarray-item',
40444         cn : [ 
40445             { tag: 'div' },
40446             {
40447                 tag: 'img',
40448                 width:16,
40449                 height : 16,
40450                 src : Roo.BLANK_IMAGE_URL ,
40451                 align: 'center'
40452             }
40453         ]
40454         
40455     },
40456     
40457  
40458     onRender : function(ct, position)
40459     {
40460         Roo.form.Field.superclass.onRender.call(this, ct, position);
40461         
40462         if(!this.el){
40463             var cfg = this.getAutoCreate();
40464             this.el = ct.createChild(cfg, position);
40465         }
40466         
40467         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40468         
40469         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40470             this.cb.renderer(this.data) :
40471             String.format('{0}',this.data[this.displayField]);
40472         
40473             
40474         this.el.child('div').dom.setAttribute('qtip',
40475                         String.format('{0}',this.data[this.tipField])
40476         );
40477         
40478         this.el.child('img').on('click', this.remove, this);
40479         
40480     },
40481    
40482     remove : function()
40483     {
40484         
40485         this.cb.items.remove(this);
40486         this.el.child('img').un('click', this.remove, this);
40487         this.el.remove();
40488         this.cb.updateHiddenEl();
40489     }
40490     
40491 });/*
40492  * Based on:
40493  * Ext JS Library 1.1.1
40494  * Copyright(c) 2006-2007, Ext JS, LLC.
40495  *
40496  * Originally Released Under LGPL - original licence link has changed is not relivant.
40497  *
40498  * Fork - LGPL
40499  * <script type="text/javascript">
40500  */
40501 /**
40502  * @class Roo.form.Checkbox
40503  * @extends Roo.form.Field
40504  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40505  * @constructor
40506  * Creates a new Checkbox
40507  * @param {Object} config Configuration options
40508  */
40509 Roo.form.Checkbox = function(config){
40510     Roo.form.Checkbox.superclass.constructor.call(this, config);
40511     this.addEvents({
40512         /**
40513          * @event check
40514          * Fires when the checkbox is checked or unchecked.
40515              * @param {Roo.form.Checkbox} this This checkbox
40516              * @param {Boolean} checked The new checked value
40517              */
40518         check : true
40519     });
40520 };
40521
40522 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40523     /**
40524      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40525      */
40526     focusClass : undefined,
40527     /**
40528      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40529      */
40530     fieldClass: "x-form-field",
40531     /**
40532      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40533      */
40534     checked: false,
40535     /**
40536      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40537      * {tag: "input", type: "checkbox", autocomplete: "off"})
40538      */
40539     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40540     /**
40541      * @cfg {String} boxLabel The text that appears beside the checkbox
40542      */
40543     boxLabel : "",
40544     /**
40545      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40546      */  
40547     inputValue : '1',
40548     /**
40549      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40550      */
40551      valueOff: '0', // value when not checked..
40552
40553     actionMode : 'viewEl', 
40554     //
40555     // private
40556     itemCls : 'x-menu-check-item x-form-item',
40557     groupClass : 'x-menu-group-item',
40558     inputType : 'hidden',
40559     
40560     
40561     inSetChecked: false, // check that we are not calling self...
40562     
40563     inputElement: false, // real input element?
40564     basedOn: false, // ????
40565     
40566     isFormField: true, // not sure where this is needed!!!!
40567
40568     onResize : function(){
40569         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40570         if(!this.boxLabel){
40571             this.el.alignTo(this.wrap, 'c-c');
40572         }
40573     },
40574
40575     initEvents : function(){
40576         Roo.form.Checkbox.superclass.initEvents.call(this);
40577         this.el.on("click", this.onClick,  this);
40578         this.el.on("change", this.onClick,  this);
40579     },
40580
40581
40582     getResizeEl : function(){
40583         return this.wrap;
40584     },
40585
40586     getPositionEl : function(){
40587         return this.wrap;
40588     },
40589
40590     // private
40591     onRender : function(ct, position){
40592         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40593         /*
40594         if(this.inputValue !== undefined){
40595             this.el.dom.value = this.inputValue;
40596         }
40597         */
40598         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40599         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40600         var viewEl = this.wrap.createChild({ 
40601             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40602         this.viewEl = viewEl;   
40603         this.wrap.on('click', this.onClick,  this); 
40604         
40605         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40606         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40607         
40608         
40609         
40610         if(this.boxLabel){
40611             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40612         //    viewEl.on('click', this.onClick,  this); 
40613         }
40614         //if(this.checked){
40615             this.setChecked(this.checked);
40616         //}else{
40617             //this.checked = this.el.dom;
40618         //}
40619
40620     },
40621
40622     // private
40623     initValue : Roo.emptyFn,
40624
40625     /**
40626      * Returns the checked state of the checkbox.
40627      * @return {Boolean} True if checked, else false
40628      */
40629     getValue : function(){
40630         if(this.el){
40631             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40632         }
40633         return this.valueOff;
40634         
40635     },
40636
40637         // private
40638     onClick : function(){ 
40639         this.setChecked(!this.checked);
40640
40641         //if(this.el.dom.checked != this.checked){
40642         //    this.setValue(this.el.dom.checked);
40643        // }
40644     },
40645
40646     /**
40647      * Sets the checked state of the checkbox.
40648      * On is always based on a string comparison between inputValue and the param.
40649      * @param {Boolean/String} value - the value to set 
40650      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40651      */
40652     setValue : function(v,suppressEvent){
40653         
40654         
40655         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40656         //if(this.el && this.el.dom){
40657         //    this.el.dom.checked = this.checked;
40658         //    this.el.dom.defaultChecked = this.checked;
40659         //}
40660         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40661         //this.fireEvent("check", this, this.checked);
40662     },
40663     // private..
40664     setChecked : function(state,suppressEvent)
40665     {
40666         if (this.inSetChecked) {
40667             this.checked = state;
40668             return;
40669         }
40670         
40671     
40672         if(this.wrap){
40673             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40674         }
40675         this.checked = state;
40676         if(suppressEvent !== true){
40677             this.fireEvent('check', this, state);
40678         }
40679         this.inSetChecked = true;
40680         this.el.dom.value = state ? this.inputValue : this.valueOff;
40681         this.inSetChecked = false;
40682         
40683     },
40684     // handle setting of hidden value by some other method!!?!?
40685     setFromHidden: function()
40686     {
40687         if(!this.el){
40688             return;
40689         }
40690         //console.log("SET FROM HIDDEN");
40691         //alert('setFrom hidden');
40692         this.setValue(this.el.dom.value);
40693     },
40694     
40695     onDestroy : function()
40696     {
40697         if(this.viewEl){
40698             Roo.get(this.viewEl).remove();
40699         }
40700          
40701         Roo.form.Checkbox.superclass.onDestroy.call(this);
40702     }
40703
40704 });/*
40705  * Based on:
40706  * Ext JS Library 1.1.1
40707  * Copyright(c) 2006-2007, Ext JS, LLC.
40708  *
40709  * Originally Released Under LGPL - original licence link has changed is not relivant.
40710  *
40711  * Fork - LGPL
40712  * <script type="text/javascript">
40713  */
40714  
40715 /**
40716  * @class Roo.form.Radio
40717  * @extends Roo.form.Checkbox
40718  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40719  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40720  * @constructor
40721  * Creates a new Radio
40722  * @param {Object} config Configuration options
40723  */
40724 Roo.form.Radio = function(){
40725     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40726 };
40727 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40728     inputType: 'radio',
40729
40730     /**
40731      * If this radio is part of a group, it will return the selected value
40732      * @return {String}
40733      */
40734     getGroupValue : function(){
40735         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40736     },
40737     
40738     
40739     onRender : function(ct, position){
40740         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40741         
40742         if(this.inputValue !== undefined){
40743             this.el.dom.value = this.inputValue;
40744         }
40745          
40746         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40747         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40748         //var viewEl = this.wrap.createChild({ 
40749         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40750         //this.viewEl = viewEl;   
40751         //this.wrap.on('click', this.onClick,  this); 
40752         
40753         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40754         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40755         
40756         
40757         
40758         if(this.boxLabel){
40759             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40760         //    viewEl.on('click', this.onClick,  this); 
40761         }
40762          if(this.checked){
40763             this.el.dom.checked =   'checked' ;
40764         }
40765          
40766     } 
40767     
40768     
40769 });//<script type="text/javascript">
40770
40771 /*
40772  * Ext JS Library 1.1.1
40773  * Copyright(c) 2006-2007, Ext JS, LLC.
40774  * licensing@extjs.com
40775  * 
40776  * http://www.extjs.com/license
40777  */
40778  
40779  /*
40780   * 
40781   * Known bugs:
40782   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40783   * - IE ? - no idea how much works there.
40784   * 
40785   * 
40786   * 
40787   */
40788  
40789
40790 /**
40791  * @class Ext.form.HtmlEditor
40792  * @extends Ext.form.Field
40793  * Provides a lightweight HTML Editor component.
40794  *
40795  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40796  * 
40797  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40798  * supported by this editor.</b><br/><br/>
40799  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40800  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40801  */
40802 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40803       /**
40804      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40805      */
40806     toolbars : false,
40807     /**
40808      * @cfg {String} createLinkText The default text for the create link prompt
40809      */
40810     createLinkText : 'Please enter the URL for the link:',
40811     /**
40812      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40813      */
40814     defaultLinkValue : 'http:/'+'/',
40815    
40816      /**
40817      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40818      *                        Roo.resizable.
40819      */
40820     resizable : false,
40821      /**
40822      * @cfg {Number} height (in pixels)
40823      */   
40824     height: 300,
40825    /**
40826      * @cfg {Number} width (in pixels)
40827      */   
40828     width: 500,
40829     
40830     /**
40831      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40832      * 
40833      */
40834     stylesheets: false,
40835     
40836     // id of frame..
40837     frameId: false,
40838     
40839     // private properties
40840     validationEvent : false,
40841     deferHeight: true,
40842     initialized : false,
40843     activated : false,
40844     sourceEditMode : false,
40845     onFocus : Roo.emptyFn,
40846     iframePad:3,
40847     hideMode:'offsets',
40848     
40849     defaultAutoCreate : { // modified by initCompnoent..
40850         tag: "textarea",
40851         style:"width:500px;height:300px;",
40852         autocomplete: "off"
40853     },
40854
40855     // private
40856     initComponent : function(){
40857         this.addEvents({
40858             /**
40859              * @event initialize
40860              * Fires when the editor is fully initialized (including the iframe)
40861              * @param {HtmlEditor} this
40862              */
40863             initialize: true,
40864             /**
40865              * @event activate
40866              * Fires when the editor is first receives the focus. Any insertion must wait
40867              * until after this event.
40868              * @param {HtmlEditor} this
40869              */
40870             activate: true,
40871              /**
40872              * @event beforesync
40873              * Fires before the textarea is updated with content from the editor iframe. Return false
40874              * to cancel the sync.
40875              * @param {HtmlEditor} this
40876              * @param {String} html
40877              */
40878             beforesync: true,
40879              /**
40880              * @event beforepush
40881              * Fires before the iframe editor is updated with content from the textarea. Return false
40882              * to cancel the push.
40883              * @param {HtmlEditor} this
40884              * @param {String} html
40885              */
40886             beforepush: true,
40887              /**
40888              * @event sync
40889              * Fires when the textarea is updated with content from the editor iframe.
40890              * @param {HtmlEditor} this
40891              * @param {String} html
40892              */
40893             sync: true,
40894              /**
40895              * @event push
40896              * Fires when the iframe editor is updated with content from the textarea.
40897              * @param {HtmlEditor} this
40898              * @param {String} html
40899              */
40900             push: true,
40901              /**
40902              * @event editmodechange
40903              * Fires when the editor switches edit modes
40904              * @param {HtmlEditor} this
40905              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40906              */
40907             editmodechange: true,
40908             /**
40909              * @event editorevent
40910              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40911              * @param {HtmlEditor} this
40912              */
40913             editorevent: true
40914         });
40915         this.defaultAutoCreate =  {
40916             tag: "textarea",
40917             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40918             autocomplete: "off"
40919         };
40920     },
40921
40922     /**
40923      * Protected method that will not generally be called directly. It
40924      * is called when the editor creates its toolbar. Override this method if you need to
40925      * add custom toolbar buttons.
40926      * @param {HtmlEditor} editor
40927      */
40928     createToolbar : function(editor){
40929         if (!editor.toolbars || !editor.toolbars.length) {
40930             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40931         }
40932         
40933         for (var i =0 ; i < editor.toolbars.length;i++) {
40934             editor.toolbars[i] = Roo.factory(
40935                     typeof(editor.toolbars[i]) == 'string' ?
40936                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40937                 Roo.form.HtmlEditor);
40938             editor.toolbars[i].init(editor);
40939         }
40940          
40941         
40942     },
40943
40944     /**
40945      * Protected method that will not generally be called directly. It
40946      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40947      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40948      */
40949     getDocMarkup : function(){
40950         // body styles..
40951         var st = '';
40952         if (this.stylesheets === false) {
40953             
40954             Roo.get(document.head).select('style').each(function(node) {
40955                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40956             });
40957             
40958             Roo.get(document.head).select('link').each(function(node) { 
40959                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40960             });
40961             
40962         } else if (!this.stylesheets.length) {
40963                 // simple..
40964                 st = '<style type="text/css">' +
40965                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40966                    '</style>';
40967         } else {
40968             Roo.each(this.stylesheets, function(s) {
40969                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40970             });
40971             
40972         }
40973         
40974         st +=  '<style type="text/css">' +
40975             'IMG { cursor: pointer } ' +
40976         '</style>';
40977
40978         
40979         return '<html><head>' + st  +
40980             //<style type="text/css">' +
40981             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40982             //'</style>' +
40983             ' </head><body class="roo-htmleditor-body"></body></html>';
40984     },
40985
40986     // private
40987     onRender : function(ct, position)
40988     {
40989         var _t = this;
40990         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40991         this.el.dom.style.border = '0 none';
40992         this.el.dom.setAttribute('tabIndex', -1);
40993         this.el.addClass('x-hidden');
40994         if(Roo.isIE){ // fix IE 1px bogus margin
40995             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40996         }
40997         this.wrap = this.el.wrap({
40998             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40999         });
41000         
41001         if (this.resizable) {
41002             this.resizeEl = new Roo.Resizable(this.wrap, {
41003                 pinned : true,
41004                 wrap: true,
41005                 dynamic : true,
41006                 minHeight : this.height,
41007                 height: this.height,
41008                 handles : this.resizable,
41009                 width: this.width,
41010                 listeners : {
41011                     resize : function(r, w, h) {
41012                         _t.onResize(w,h); // -something
41013                     }
41014                 }
41015             });
41016             
41017         }
41018
41019         this.frameId = Roo.id();
41020         
41021         this.createToolbar(this);
41022         
41023       
41024         
41025         var iframe = this.wrap.createChild({
41026             tag: 'iframe',
41027             id: this.frameId,
41028             name: this.frameId,
41029             frameBorder : 'no',
41030             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41031         }, this.el
41032         );
41033         
41034        // console.log(iframe);
41035         //this.wrap.dom.appendChild(iframe);
41036
41037         this.iframe = iframe.dom;
41038
41039          this.assignDocWin();
41040         
41041         this.doc.designMode = 'on';
41042        
41043         this.doc.open();
41044         this.doc.write(this.getDocMarkup());
41045         this.doc.close();
41046
41047         
41048         var task = { // must defer to wait for browser to be ready
41049             run : function(){
41050                 //console.log("run task?" + this.doc.readyState);
41051                 this.assignDocWin();
41052                 if(this.doc.body || this.doc.readyState == 'complete'){
41053                     try {
41054                         this.doc.designMode="on";
41055                     } catch (e) {
41056                         return;
41057                     }
41058                     Roo.TaskMgr.stop(task);
41059                     this.initEditor.defer(10, this);
41060                 }
41061             },
41062             interval : 10,
41063             duration:10000,
41064             scope: this
41065         };
41066         Roo.TaskMgr.start(task);
41067
41068         if(!this.width){
41069             this.setSize(this.wrap.getSize());
41070         }
41071         if (this.resizeEl) {
41072             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41073             // should trigger onReize..
41074         }
41075     },
41076
41077     // private
41078     onResize : function(w, h)
41079     {
41080         //Roo.log('resize: ' +w + ',' + h );
41081         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41082         if(this.el && this.iframe){
41083             if(typeof w == 'number'){
41084                 var aw = w - this.wrap.getFrameWidth('lr');
41085                 this.el.setWidth(this.adjustWidth('textarea', aw));
41086                 this.iframe.style.width = aw + 'px';
41087             }
41088             if(typeof h == 'number'){
41089                 var tbh = 0;
41090                 for (var i =0; i < this.toolbars.length;i++) {
41091                     // fixme - ask toolbars for heights?
41092                     tbh += this.toolbars[i].tb.el.getHeight();
41093                     if (this.toolbars[i].footer) {
41094                         tbh += this.toolbars[i].footer.el.getHeight();
41095                     }
41096                 }
41097                 
41098                 
41099                 
41100                 
41101                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41102                 ah -= 5; // knock a few pixes off for look..
41103                 this.el.setHeight(this.adjustWidth('textarea', ah));
41104                 this.iframe.style.height = ah + 'px';
41105                 if(this.doc){
41106                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41107                 }
41108             }
41109         }
41110     },
41111
41112     /**
41113      * Toggles the editor between standard and source edit mode.
41114      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41115      */
41116     toggleSourceEdit : function(sourceEditMode){
41117         
41118         this.sourceEditMode = sourceEditMode === true;
41119         
41120         if(this.sourceEditMode){
41121 //            Roo.log('in');
41122 //            Roo.log(this.syncValue());
41123             this.syncValue();
41124             this.iframe.className = 'x-hidden';
41125             this.el.removeClass('x-hidden');
41126             this.el.dom.removeAttribute('tabIndex');
41127             this.el.focus();
41128         }else{
41129 //            Roo.log('out')
41130 //            Roo.log(this.pushValue()); 
41131             this.pushValue();
41132             this.iframe.className = '';
41133             this.el.addClass('x-hidden');
41134             this.el.dom.setAttribute('tabIndex', -1);
41135             this.deferFocus();
41136         }
41137         this.setSize(this.wrap.getSize());
41138         this.fireEvent('editmodechange', this, this.sourceEditMode);
41139     },
41140
41141     // private used internally
41142     createLink : function(){
41143         var url = prompt(this.createLinkText, this.defaultLinkValue);
41144         if(url && url != 'http:/'+'/'){
41145             this.relayCmd('createlink', url);
41146         }
41147     },
41148
41149     // private (for BoxComponent)
41150     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41151
41152     // private (for BoxComponent)
41153     getResizeEl : function(){
41154         return this.wrap;
41155     },
41156
41157     // private (for BoxComponent)
41158     getPositionEl : function(){
41159         return this.wrap;
41160     },
41161
41162     // private
41163     initEvents : function(){
41164         this.originalValue = this.getValue();
41165     },
41166
41167     /**
41168      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41169      * @method
41170      */
41171     markInvalid : Roo.emptyFn,
41172     /**
41173      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41174      * @method
41175      */
41176     clearInvalid : Roo.emptyFn,
41177
41178     setValue : function(v){
41179         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41180         this.pushValue();
41181     },
41182
41183     /**
41184      * Protected method that will not generally be called directly. If you need/want
41185      * custom HTML cleanup, this is the method you should override.
41186      * @param {String} html The HTML to be cleaned
41187      * return {String} The cleaned HTML
41188      */
41189     cleanHtml : function(html){
41190         html = String(html);
41191         if(html.length > 5){
41192             if(Roo.isSafari){ // strip safari nonsense
41193                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41194             }
41195         }
41196         if(html == '&nbsp;'){
41197             html = '';
41198         }
41199         return html;
41200     },
41201
41202     /**
41203      * Protected method that will not generally be called directly. Syncs the contents
41204      * of the editor iframe with the textarea.
41205      */
41206     syncValue : function(){
41207         if(this.initialized){
41208             var bd = (this.doc.body || this.doc.documentElement);
41209             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41210             var html = bd.innerHTML;
41211             if(Roo.isSafari){
41212                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41213                 var m = bs.match(/text-align:(.*?);/i);
41214                 if(m && m[1]){
41215                     html = '<div style="'+m[0]+'">' + html + '</div>';
41216                 }
41217             }
41218             html = this.cleanHtml(html);
41219             // fix up the special chars.. normaly like back quotes in word...
41220             // however we do not want to do this with chinese..
41221             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41222                 var cc = b.charCodeAt();
41223                 if (
41224                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41225                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41226                     (cc >= 0xf900 && cc < 0xfb00 )
41227                 ) {
41228                         return b;
41229                 }
41230                 return "&#"+cc+";" 
41231             });
41232             if(this.fireEvent('beforesync', this, html) !== false){
41233                 this.el.dom.value = html;
41234                 this.fireEvent('sync', this, html);
41235             }
41236         }
41237     },
41238
41239     /**
41240      * Protected method that will not generally be called directly. Pushes the value of the textarea
41241      * into the iframe editor.
41242      */
41243     pushValue : function(){
41244         if(this.initialized){
41245             var v = this.el.dom.value;
41246             
41247             if(v.length < 1){
41248                 v = '&#160;';
41249             }
41250             
41251             if(this.fireEvent('beforepush', this, v) !== false){
41252                 var d = (this.doc.body || this.doc.documentElement);
41253                 d.innerHTML = v;
41254                 this.cleanUpPaste();
41255                 this.el.dom.value = d.innerHTML;
41256                 this.fireEvent('push', this, v);
41257             }
41258         }
41259     },
41260
41261     // private
41262     deferFocus : function(){
41263         this.focus.defer(10, this);
41264     },
41265
41266     // doc'ed in Field
41267     focus : function(){
41268         if(this.win && !this.sourceEditMode){
41269             this.win.focus();
41270         }else{
41271             this.el.focus();
41272         }
41273     },
41274     
41275     assignDocWin: function()
41276     {
41277         var iframe = this.iframe;
41278         
41279          if(Roo.isIE){
41280             this.doc = iframe.contentWindow.document;
41281             this.win = iframe.contentWindow;
41282         } else {
41283             if (!Roo.get(this.frameId)) {
41284                 return;
41285             }
41286             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41287             this.win = Roo.get(this.frameId).dom.contentWindow;
41288         }
41289     },
41290     
41291     // private
41292     initEditor : function(){
41293         //console.log("INIT EDITOR");
41294         this.assignDocWin();
41295         
41296         
41297         
41298         this.doc.designMode="on";
41299         this.doc.open();
41300         this.doc.write(this.getDocMarkup());
41301         this.doc.close();
41302         
41303         var dbody = (this.doc.body || this.doc.documentElement);
41304         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41305         // this copies styles from the containing element into thsi one..
41306         // not sure why we need all of this..
41307         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41308         ss['background-attachment'] = 'fixed'; // w3c
41309         dbody.bgProperties = 'fixed'; // ie
41310         Roo.DomHelper.applyStyles(dbody, ss);
41311         Roo.EventManager.on(this.doc, {
41312             //'mousedown': this.onEditorEvent,
41313             'mouseup': this.onEditorEvent,
41314             'dblclick': this.onEditorEvent,
41315             'click': this.onEditorEvent,
41316             'keyup': this.onEditorEvent,
41317             buffer:100,
41318             scope: this
41319         });
41320         if(Roo.isGecko){
41321             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41322         }
41323         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41324             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41325         }
41326         this.initialized = true;
41327
41328         this.fireEvent('initialize', this);
41329         this.pushValue();
41330     },
41331
41332     // private
41333     onDestroy : function(){
41334         
41335         
41336         
41337         if(this.rendered){
41338             
41339             for (var i =0; i < this.toolbars.length;i++) {
41340                 // fixme - ask toolbars for heights?
41341                 this.toolbars[i].onDestroy();
41342             }
41343             
41344             this.wrap.dom.innerHTML = '';
41345             this.wrap.remove();
41346         }
41347     },
41348
41349     // private
41350     onFirstFocus : function(){
41351         
41352         this.assignDocWin();
41353         
41354         
41355         this.activated = true;
41356         for (var i =0; i < this.toolbars.length;i++) {
41357             this.toolbars[i].onFirstFocus();
41358         }
41359        
41360         if(Roo.isGecko){ // prevent silly gecko errors
41361             this.win.focus();
41362             var s = this.win.getSelection();
41363             if(!s.focusNode || s.focusNode.nodeType != 3){
41364                 var r = s.getRangeAt(0);
41365                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41366                 r.collapse(true);
41367                 this.deferFocus();
41368             }
41369             try{
41370                 this.execCmd('useCSS', true);
41371                 this.execCmd('styleWithCSS', false);
41372             }catch(e){}
41373         }
41374         this.fireEvent('activate', this);
41375     },
41376
41377     // private
41378     adjustFont: function(btn){
41379         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41380         //if(Roo.isSafari){ // safari
41381         //    adjust *= 2;
41382        // }
41383         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41384         if(Roo.isSafari){ // safari
41385             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41386             v =  (v < 10) ? 10 : v;
41387             v =  (v > 48) ? 48 : v;
41388             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41389             
41390         }
41391         
41392         
41393         v = Math.max(1, v+adjust);
41394         
41395         this.execCmd('FontSize', v  );
41396     },
41397
41398     onEditorEvent : function(e){
41399         this.fireEvent('editorevent', this, e);
41400       //  this.updateToolbar();
41401         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41402     },
41403
41404     insertTag : function(tg)
41405     {
41406         // could be a bit smarter... -> wrap the current selected tRoo..
41407         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41408             
41409             range = this.createRange(this.getSelection());
41410             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41411             wrappingNode.appendChild(range.extractContents());
41412             range.insertNode(wrappingNode);
41413
41414             return;
41415             
41416             
41417             
41418         }
41419         this.execCmd("formatblock",   tg);
41420         
41421     },
41422     
41423     insertText : function(txt)
41424     {
41425         
41426         
41427         var range = this.createRange();
41428         range.deleteContents();
41429                //alert(Sender.getAttribute('label'));
41430                
41431         range.insertNode(this.doc.createTextNode(txt));
41432     } ,
41433     
41434     // private
41435     relayBtnCmd : function(btn){
41436         this.relayCmd(btn.cmd);
41437     },
41438
41439     /**
41440      * Executes a Midas editor command on the editor document and performs necessary focus and
41441      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41442      * @param {String} cmd The Midas command
41443      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41444      */
41445     relayCmd : function(cmd, value){
41446         this.win.focus();
41447         this.execCmd(cmd, value);
41448         this.fireEvent('editorevent', this);
41449         //this.updateToolbar();
41450         this.deferFocus();
41451     },
41452
41453     /**
41454      * Executes a Midas editor command directly on the editor document.
41455      * For visual commands, you should use {@link #relayCmd} instead.
41456      * <b>This should only be called after the editor is initialized.</b>
41457      * @param {String} cmd The Midas command
41458      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41459      */
41460     execCmd : function(cmd, value){
41461         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41462         this.syncValue();
41463     },
41464  
41465  
41466    
41467     /**
41468      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41469      * to insert tRoo.
41470      * @param {String} text | dom node.. 
41471      */
41472     insertAtCursor : function(text)
41473     {
41474         
41475         
41476         
41477         if(!this.activated){
41478             return;
41479         }
41480         /*
41481         if(Roo.isIE){
41482             this.win.focus();
41483             var r = this.doc.selection.createRange();
41484             if(r){
41485                 r.collapse(true);
41486                 r.pasteHTML(text);
41487                 this.syncValue();
41488                 this.deferFocus();
41489             
41490             }
41491             return;
41492         }
41493         */
41494         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41495             this.win.focus();
41496             
41497             
41498             // from jquery ui (MIT licenced)
41499             var range, node;
41500             var win = this.win;
41501             
41502             if (win.getSelection && win.getSelection().getRangeAt) {
41503                 range = win.getSelection().getRangeAt(0);
41504                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41505                 range.insertNode(node);
41506             } else if (win.document.selection && win.document.selection.createRange) {
41507                 // no firefox support
41508                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41509                 win.document.selection.createRange().pasteHTML(txt);
41510             } else {
41511                 // no firefox support
41512                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41513                 this.execCmd('InsertHTML', txt);
41514             } 
41515             
41516             this.syncValue();
41517             
41518             this.deferFocus();
41519         }
41520     },
41521  // private
41522     mozKeyPress : function(e){
41523         if(e.ctrlKey){
41524             var c = e.getCharCode(), cmd;
41525           
41526             if(c > 0){
41527                 c = String.fromCharCode(c).toLowerCase();
41528                 switch(c){
41529                     case 'b':
41530                         cmd = 'bold';
41531                         break;
41532                     case 'i':
41533                         cmd = 'italic';
41534                         break;
41535                     
41536                     case 'u':
41537                         cmd = 'underline';
41538                         break;
41539                     
41540                     case 'v':
41541                         this.cleanUpPaste.defer(100, this);
41542                         return;
41543                         
41544                 }
41545                 if(cmd){
41546                     this.win.focus();
41547                     this.execCmd(cmd);
41548                     this.deferFocus();
41549                     e.preventDefault();
41550                 }
41551                 
41552             }
41553         }
41554     },
41555
41556     // private
41557     fixKeys : function(){ // load time branching for fastest keydown performance
41558         if(Roo.isIE){
41559             return function(e){
41560                 var k = e.getKey(), r;
41561                 if(k == e.TAB){
41562                     e.stopEvent();
41563                     r = this.doc.selection.createRange();
41564                     if(r){
41565                         r.collapse(true);
41566                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41567                         this.deferFocus();
41568                     }
41569                     return;
41570                 }
41571                 
41572                 if(k == e.ENTER){
41573                     r = this.doc.selection.createRange();
41574                     if(r){
41575                         var target = r.parentElement();
41576                         if(!target || target.tagName.toLowerCase() != 'li'){
41577                             e.stopEvent();
41578                             r.pasteHTML('<br />');
41579                             r.collapse(false);
41580                             r.select();
41581                         }
41582                     }
41583                 }
41584                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41585                     this.cleanUpPaste.defer(100, this);
41586                     return;
41587                 }
41588                 
41589                 
41590             };
41591         }else if(Roo.isOpera){
41592             return function(e){
41593                 var k = e.getKey();
41594                 if(k == e.TAB){
41595                     e.stopEvent();
41596                     this.win.focus();
41597                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41598                     this.deferFocus();
41599                 }
41600                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41601                     this.cleanUpPaste.defer(100, this);
41602                     return;
41603                 }
41604                 
41605             };
41606         }else if(Roo.isSafari){
41607             return function(e){
41608                 var k = e.getKey();
41609                 
41610                 if(k == e.TAB){
41611                     e.stopEvent();
41612                     this.execCmd('InsertText','\t');
41613                     this.deferFocus();
41614                     return;
41615                 }
41616                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41617                     this.cleanUpPaste.defer(100, this);
41618                     return;
41619                 }
41620                 
41621              };
41622         }
41623     }(),
41624     
41625     getAllAncestors: function()
41626     {
41627         var p = this.getSelectedNode();
41628         var a = [];
41629         if (!p) {
41630             a.push(p); // push blank onto stack..
41631             p = this.getParentElement();
41632         }
41633         
41634         
41635         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41636             a.push(p);
41637             p = p.parentNode;
41638         }
41639         a.push(this.doc.body);
41640         return a;
41641     },
41642     lastSel : false,
41643     lastSelNode : false,
41644     
41645     
41646     getSelection : function() 
41647     {
41648         this.assignDocWin();
41649         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41650     },
41651     
41652     getSelectedNode: function() 
41653     {
41654         // this may only work on Gecko!!!
41655         
41656         // should we cache this!!!!
41657         
41658         
41659         
41660          
41661         var range = this.createRange(this.getSelection()).cloneRange();
41662         
41663         if (Roo.isIE) {
41664             var parent = range.parentElement();
41665             while (true) {
41666                 var testRange = range.duplicate();
41667                 testRange.moveToElementText(parent);
41668                 if (testRange.inRange(range)) {
41669                     break;
41670                 }
41671                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41672                     break;
41673                 }
41674                 parent = parent.parentElement;
41675             }
41676             return parent;
41677         }
41678         
41679         // is ancestor a text element.
41680         var ac =  range.commonAncestorContainer;
41681         if (ac.nodeType == 3) {
41682             ac = ac.parentNode;
41683         }
41684         
41685         var ar = ac.childNodes;
41686          
41687         var nodes = [];
41688         var other_nodes = [];
41689         var has_other_nodes = false;
41690         for (var i=0;i<ar.length;i++) {
41691             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41692                 continue;
41693             }
41694             // fullly contained node.
41695             
41696             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41697                 nodes.push(ar[i]);
41698                 continue;
41699             }
41700             
41701             // probably selected..
41702             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41703                 other_nodes.push(ar[i]);
41704                 continue;
41705             }
41706             // outer..
41707             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41708                 continue;
41709             }
41710             
41711             
41712             has_other_nodes = true;
41713         }
41714         if (!nodes.length && other_nodes.length) {
41715             nodes= other_nodes;
41716         }
41717         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41718             return false;
41719         }
41720         
41721         return nodes[0];
41722     },
41723     createRange: function(sel)
41724     {
41725         // this has strange effects when using with 
41726         // top toolbar - not sure if it's a great idea.
41727         //this.editor.contentWindow.focus();
41728         if (typeof sel != "undefined") {
41729             try {
41730                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41731             } catch(e) {
41732                 return this.doc.createRange();
41733             }
41734         } else {
41735             return this.doc.createRange();
41736         }
41737     },
41738     getParentElement: function()
41739     {
41740         
41741         this.assignDocWin();
41742         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41743         
41744         var range = this.createRange(sel);
41745          
41746         try {
41747             var p = range.commonAncestorContainer;
41748             while (p.nodeType == 3) { // text node
41749                 p = p.parentNode;
41750             }
41751             return p;
41752         } catch (e) {
41753             return null;
41754         }
41755     
41756     },
41757     /***
41758      *
41759      * Range intersection.. the hard stuff...
41760      *  '-1' = before
41761      *  '0' = hits..
41762      *  '1' = after.
41763      *         [ -- selected range --- ]
41764      *   [fail]                        [fail]
41765      *
41766      *    basically..
41767      *      if end is before start or  hits it. fail.
41768      *      if start is after end or hits it fail.
41769      *
41770      *   if either hits (but other is outside. - then it's not 
41771      *   
41772      *    
41773      **/
41774     
41775     
41776     // @see http://www.thismuchiknow.co.uk/?p=64.
41777     rangeIntersectsNode : function(range, node)
41778     {
41779         var nodeRange = node.ownerDocument.createRange();
41780         try {
41781             nodeRange.selectNode(node);
41782         } catch (e) {
41783             nodeRange.selectNodeContents(node);
41784         }
41785     
41786         var rangeStartRange = range.cloneRange();
41787         rangeStartRange.collapse(true);
41788     
41789         var rangeEndRange = range.cloneRange();
41790         rangeEndRange.collapse(false);
41791     
41792         var nodeStartRange = nodeRange.cloneRange();
41793         nodeStartRange.collapse(true);
41794     
41795         var nodeEndRange = nodeRange.cloneRange();
41796         nodeEndRange.collapse(false);
41797     
41798         return rangeStartRange.compareBoundaryPoints(
41799                  Range.START_TO_START, nodeEndRange) == -1 &&
41800                rangeEndRange.compareBoundaryPoints(
41801                  Range.START_TO_START, nodeStartRange) == 1;
41802         
41803          
41804     },
41805     rangeCompareNode : function(range, node)
41806     {
41807         var nodeRange = node.ownerDocument.createRange();
41808         try {
41809             nodeRange.selectNode(node);
41810         } catch (e) {
41811             nodeRange.selectNodeContents(node);
41812         }
41813         
41814         
41815         range.collapse(true);
41816     
41817         nodeRange.collapse(true);
41818      
41819         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41820         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41821          
41822         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41823         
41824         var nodeIsBefore   =  ss == 1;
41825         var nodeIsAfter    = ee == -1;
41826         
41827         if (nodeIsBefore && nodeIsAfter)
41828             return 0; // outer
41829         if (!nodeIsBefore && nodeIsAfter)
41830             return 1; //right trailed.
41831         
41832         if (nodeIsBefore && !nodeIsAfter)
41833             return 2;  // left trailed.
41834         // fully contined.
41835         return 3;
41836     },
41837
41838     // private? - in a new class?
41839     cleanUpPaste :  function()
41840     {
41841         // cleans up the whole document..
41842          Roo.log('cleanuppaste');
41843         this.cleanUpChildren(this.doc.body);
41844         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41845         if (clean != this.doc.body.innerHTML) {
41846             this.doc.body.innerHTML = clean;
41847         }
41848         
41849     },
41850     
41851     cleanWordChars : function(input) {// change the chars to hex code
41852         var he = Roo.form.HtmlEditor;
41853         
41854         var output = input;
41855         Roo.each(he.swapCodes, function(sw) { 
41856             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41857             
41858             output = output.replace(swapper, sw[1]);
41859         });
41860         
41861         return output;
41862     },
41863     
41864     
41865     cleanUpChildren : function (n)
41866     {
41867         if (!n.childNodes.length) {
41868             return;
41869         }
41870         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41871            this.cleanUpChild(n.childNodes[i]);
41872         }
41873     },
41874     
41875     
41876         
41877     
41878     cleanUpChild : function (node)
41879     {
41880         var ed = this;
41881         //console.log(node);
41882         if (node.nodeName == "#text") {
41883             // clean up silly Windows -- stuff?
41884             return; 
41885         }
41886         if (node.nodeName == "#comment") {
41887             node.parentNode.removeChild(node);
41888             // clean up silly Windows -- stuff?
41889             return; 
41890         }
41891         
41892         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41893             // remove node.
41894             node.parentNode.removeChild(node);
41895             return;
41896             
41897         }
41898         
41899         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41900         
41901         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41902         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41903         
41904         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41905         //    remove_keep_children = true;
41906         //}
41907         
41908         if (remove_keep_children) {
41909             this.cleanUpChildren(node);
41910             // inserts everything just before this node...
41911             while (node.childNodes.length) {
41912                 var cn = node.childNodes[0];
41913                 node.removeChild(cn);
41914                 node.parentNode.insertBefore(cn, node);
41915             }
41916             node.parentNode.removeChild(node);
41917             return;
41918         }
41919         
41920         if (!node.attributes || !node.attributes.length) {
41921             this.cleanUpChildren(node);
41922             return;
41923         }
41924         
41925         function cleanAttr(n,v)
41926         {
41927             
41928             if (v.match(/^\./) || v.match(/^\//)) {
41929                 return;
41930             }
41931             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41932                 return;
41933             }
41934             if (v.match(/^#/)) {
41935                 return;
41936             }
41937 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41938             node.removeAttribute(n);
41939             
41940         }
41941         
41942         function cleanStyle(n,v)
41943         {
41944             if (v.match(/expression/)) { //XSS?? should we even bother..
41945                 node.removeAttribute(n);
41946                 return;
41947             }
41948             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41949             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41950             
41951             
41952             var parts = v.split(/;/);
41953             var clean = [];
41954             
41955             Roo.each(parts, function(p) {
41956                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41957                 if (!p.length) {
41958                     return true;
41959                 }
41960                 var l = p.split(':').shift().replace(/\s+/g,'');
41961                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41962                 
41963                 
41964                 if ( cblack.indexOf(l) > -1) {
41965 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41966                     //node.removeAttribute(n);
41967                     return true;
41968                 }
41969                 //Roo.log()
41970                 // only allow 'c whitelisted system attributes'
41971                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41972 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41973                     //node.removeAttribute(n);
41974                     return true;
41975                 }
41976                 
41977                 
41978                  
41979                 
41980                 clean.push(p);
41981                 return true;
41982             });
41983             if (clean.length) { 
41984                 node.setAttribute(n, clean.join(';'));
41985             } else {
41986                 node.removeAttribute(n);
41987             }
41988             
41989         }
41990         
41991         
41992         for (var i = node.attributes.length-1; i > -1 ; i--) {
41993             var a = node.attributes[i];
41994             //console.log(a);
41995             
41996             if (a.name.toLowerCase().substr(0,2)=='on')  {
41997                 node.removeAttribute(a.name);
41998                 continue;
41999             }
42000             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42001                 node.removeAttribute(a.name);
42002                 continue;
42003             }
42004             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42005                 cleanAttr(a.name,a.value); // fixme..
42006                 continue;
42007             }
42008             if (a.name == 'style') {
42009                 cleanStyle(a.name,a.value);
42010                 continue;
42011             }
42012             /// clean up MS crap..
42013             // tecnically this should be a list of valid class'es..
42014             
42015             
42016             if (a.name == 'class') {
42017                 if (a.value.match(/^Mso/)) {
42018                     node.className = '';
42019                 }
42020                 
42021                 if (a.value.match(/body/)) {
42022                     node.className = '';
42023                 }
42024                 continue;
42025             }
42026             
42027             // style cleanup!?
42028             // class cleanup?
42029             
42030         }
42031         
42032         
42033         this.cleanUpChildren(node);
42034         
42035         
42036     }
42037     
42038     
42039     // hide stuff that is not compatible
42040     /**
42041      * @event blur
42042      * @hide
42043      */
42044     /**
42045      * @event change
42046      * @hide
42047      */
42048     /**
42049      * @event focus
42050      * @hide
42051      */
42052     /**
42053      * @event specialkey
42054      * @hide
42055      */
42056     /**
42057      * @cfg {String} fieldClass @hide
42058      */
42059     /**
42060      * @cfg {String} focusClass @hide
42061      */
42062     /**
42063      * @cfg {String} autoCreate @hide
42064      */
42065     /**
42066      * @cfg {String} inputType @hide
42067      */
42068     /**
42069      * @cfg {String} invalidClass @hide
42070      */
42071     /**
42072      * @cfg {String} invalidText @hide
42073      */
42074     /**
42075      * @cfg {String} msgFx @hide
42076      */
42077     /**
42078      * @cfg {String} validateOnBlur @hide
42079      */
42080 });
42081
42082 Roo.form.HtmlEditor.white = [
42083         'area', 'br', 'img', 'input', 'hr', 'wbr',
42084         
42085        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42086        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42087        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42088        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42089        'table',   'ul',         'xmp', 
42090        
42091        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42092       'thead',   'tr', 
42093      
42094       'dir', 'menu', 'ol', 'ul', 'dl',
42095        
42096       'embed',  'object'
42097 ];
42098
42099
42100 Roo.form.HtmlEditor.black = [
42101     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42102         'applet', // 
42103         'base',   'basefont', 'bgsound', 'blink',  'body', 
42104         'frame',  'frameset', 'head',    'html',   'ilayer', 
42105         'iframe', 'layer',  'link',     'meta',    'object',   
42106         'script', 'style' ,'title',  'xml' // clean later..
42107 ];
42108 Roo.form.HtmlEditor.clean = [
42109     'script', 'style', 'title', 'xml'
42110 ];
42111 Roo.form.HtmlEditor.remove = [
42112     'font'
42113 ];
42114 // attributes..
42115
42116 Roo.form.HtmlEditor.ablack = [
42117     'on'
42118 ];
42119     
42120 Roo.form.HtmlEditor.aclean = [ 
42121     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42122 ];
42123
42124 // protocols..
42125 Roo.form.HtmlEditor.pwhite= [
42126         'http',  'https',  'mailto'
42127 ];
42128
42129 // white listed style attributes.
42130 Roo.form.HtmlEditor.cwhite= [
42131       //  'text-align', /// default is to allow most things..
42132       
42133          
42134 //        'font-size'//??
42135 ];
42136
42137 // black listed style attributes.
42138 Roo.form.HtmlEditor.cblack= [
42139       //  'font-size' -- this can be set by the project 
42140 ];
42141
42142
42143 Roo.form.HtmlEditor.swapCodes   =[ 
42144     [    8211, "--" ], 
42145     [    8212, "--" ], 
42146     [    8216,  "'" ],  
42147     [    8217, "'" ],  
42148     [    8220, '"' ],  
42149     [    8221, '"' ],  
42150     [    8226, "*" ],  
42151     [    8230, "..." ]
42152 ]; 
42153
42154     // <script type="text/javascript">
42155 /*
42156  * Based on
42157  * Ext JS Library 1.1.1
42158  * Copyright(c) 2006-2007, Ext JS, LLC.
42159  *  
42160  
42161  */
42162
42163 /**
42164  * @class Roo.form.HtmlEditorToolbar1
42165  * Basic Toolbar
42166  * 
42167  * Usage:
42168  *
42169  new Roo.form.HtmlEditor({
42170     ....
42171     toolbars : [
42172         new Roo.form.HtmlEditorToolbar1({
42173             disable : { fonts: 1 , format: 1, ..., ... , ...],
42174             btns : [ .... ]
42175         })
42176     }
42177      
42178  * 
42179  * @cfg {Object} disable List of elements to disable..
42180  * @cfg {Array} btns List of additional buttons.
42181  * 
42182  * 
42183  * NEEDS Extra CSS? 
42184  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42185  */
42186  
42187 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42188 {
42189     
42190     Roo.apply(this, config);
42191     
42192     // default disabled, based on 'good practice'..
42193     this.disable = this.disable || {};
42194     Roo.applyIf(this.disable, {
42195         fontSize : true,
42196         colors : true,
42197         specialElements : true
42198     });
42199     
42200     
42201     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42202     // dont call parent... till later.
42203 }
42204
42205 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42206     
42207     tb: false,
42208     
42209     rendered: false,
42210     
42211     editor : false,
42212     /**
42213      * @cfg {Object} disable  List of toolbar elements to disable
42214          
42215      */
42216     disable : false,
42217       /**
42218      * @cfg {Array} fontFamilies An array of available font families
42219      */
42220     fontFamilies : [
42221         'Arial',
42222         'Courier New',
42223         'Tahoma',
42224         'Times New Roman',
42225         'Verdana'
42226     ],
42227     
42228     specialChars : [
42229            "&#169;",
42230           "&#174;",     
42231           "&#8482;",    
42232           "&#163;" ,    
42233          // "&#8212;",    
42234           "&#8230;",    
42235           "&#247;" ,    
42236         //  "&#225;" ,     ?? a acute?
42237            "&#8364;"    , //Euro
42238        //   "&#8220;"    ,
42239         //  "&#8221;"    ,
42240         //  "&#8226;"    ,
42241           "&#176;"  //   , // degrees
42242
42243          // "&#233;"     , // e ecute
42244          // "&#250;"     , // u ecute?
42245     ],
42246     
42247     specialElements : [
42248         {
42249             text: "Insert Table",
42250             xtype: 'MenuItem',
42251             xns : Roo.Menu,
42252             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42253                 
42254         },
42255         {    
42256             text: "Insert Image",
42257             xtype: 'MenuItem',
42258             xns : Roo.Menu,
42259             ihtml : '<img src="about:blank"/>'
42260             
42261         }
42262         
42263          
42264     ],
42265     
42266     
42267     inputElements : [ 
42268             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42269             "input:submit", "input:button", "select", "textarea", "label" ],
42270     formats : [
42271         ["p"] ,  
42272         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42273         ["pre"],[ "code"], 
42274         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42275         ['div'],['span']
42276     ],
42277     
42278     cleanStyles : [
42279         "font-size"
42280     ],
42281      /**
42282      * @cfg {String} defaultFont default font to use.
42283      */
42284     defaultFont: 'tahoma',
42285    
42286     fontSelect : false,
42287     
42288     
42289     formatCombo : false,
42290     
42291     init : function(editor)
42292     {
42293         this.editor = editor;
42294         
42295         
42296         var fid = editor.frameId;
42297         var etb = this;
42298         function btn(id, toggle, handler){
42299             var xid = fid + '-'+ id ;
42300             return {
42301                 id : xid,
42302                 cmd : id,
42303                 cls : 'x-btn-icon x-edit-'+id,
42304                 enableToggle:toggle !== false,
42305                 scope: editor, // was editor...
42306                 handler:handler||editor.relayBtnCmd,
42307                 clickEvent:'mousedown',
42308                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42309                 tabIndex:-1
42310             };
42311         }
42312         
42313         
42314         
42315         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42316         this.tb = tb;
42317          // stop form submits
42318         tb.el.on('click', function(e){
42319             e.preventDefault(); // what does this do?
42320         });
42321
42322         if(!this.disable.font) { // && !Roo.isSafari){
42323             /* why no safari for fonts 
42324             editor.fontSelect = tb.el.createChild({
42325                 tag:'select',
42326                 tabIndex: -1,
42327                 cls:'x-font-select',
42328                 html: this.createFontOptions()
42329             });
42330             
42331             editor.fontSelect.on('change', function(){
42332                 var font = editor.fontSelect.dom.value;
42333                 editor.relayCmd('fontname', font);
42334                 editor.deferFocus();
42335             }, editor);
42336             
42337             tb.add(
42338                 editor.fontSelect.dom,
42339                 '-'
42340             );
42341             */
42342             
42343         };
42344         if(!this.disable.formats){
42345             this.formatCombo = new Roo.form.ComboBox({
42346                 store: new Roo.data.SimpleStore({
42347                     id : 'tag',
42348                     fields: ['tag'],
42349                     data : this.formats // from states.js
42350                 }),
42351                 blockFocus : true,
42352                 name : '',
42353                 //autoCreate : {tag: "div",  size: "20"},
42354                 displayField:'tag',
42355                 typeAhead: false,
42356                 mode: 'local',
42357                 editable : false,
42358                 triggerAction: 'all',
42359                 emptyText:'Add tag',
42360                 selectOnFocus:true,
42361                 width:135,
42362                 listeners : {
42363                     'select': function(c, r, i) {
42364                         editor.insertTag(r.get('tag'));
42365                         editor.focus();
42366                     }
42367                 }
42368
42369             });
42370             tb.addField(this.formatCombo);
42371             
42372         }
42373         
42374         if(!this.disable.format){
42375             tb.add(
42376                 btn('bold'),
42377                 btn('italic'),
42378                 btn('underline')
42379             );
42380         };
42381         if(!this.disable.fontSize){
42382             tb.add(
42383                 '-',
42384                 
42385                 
42386                 btn('increasefontsize', false, editor.adjustFont),
42387                 btn('decreasefontsize', false, editor.adjustFont)
42388             );
42389         };
42390         
42391         
42392         if(!this.disable.colors){
42393             tb.add(
42394                 '-', {
42395                     id:editor.frameId +'-forecolor',
42396                     cls:'x-btn-icon x-edit-forecolor',
42397                     clickEvent:'mousedown',
42398                     tooltip: this.buttonTips['forecolor'] || undefined,
42399                     tabIndex:-1,
42400                     menu : new Roo.menu.ColorMenu({
42401                         allowReselect: true,
42402                         focus: Roo.emptyFn,
42403                         value:'000000',
42404                         plain:true,
42405                         selectHandler: function(cp, color){
42406                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42407                             editor.deferFocus();
42408                         },
42409                         scope: editor,
42410                         clickEvent:'mousedown'
42411                     })
42412                 }, {
42413                     id:editor.frameId +'backcolor',
42414                     cls:'x-btn-icon x-edit-backcolor',
42415                     clickEvent:'mousedown',
42416                     tooltip: this.buttonTips['backcolor'] || undefined,
42417                     tabIndex:-1,
42418                     menu : new Roo.menu.ColorMenu({
42419                         focus: Roo.emptyFn,
42420                         value:'FFFFFF',
42421                         plain:true,
42422                         allowReselect: true,
42423                         selectHandler: function(cp, color){
42424                             if(Roo.isGecko){
42425                                 editor.execCmd('useCSS', false);
42426                                 editor.execCmd('hilitecolor', color);
42427                                 editor.execCmd('useCSS', true);
42428                                 editor.deferFocus();
42429                             }else{
42430                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42431                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42432                                 editor.deferFocus();
42433                             }
42434                         },
42435                         scope:editor,
42436                         clickEvent:'mousedown'
42437                     })
42438                 }
42439             );
42440         };
42441         // now add all the items...
42442         
42443
42444         if(!this.disable.alignments){
42445             tb.add(
42446                 '-',
42447                 btn('justifyleft'),
42448                 btn('justifycenter'),
42449                 btn('justifyright')
42450             );
42451         };
42452
42453         //if(!Roo.isSafari){
42454             if(!this.disable.links){
42455                 tb.add(
42456                     '-',
42457                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42458                 );
42459             };
42460
42461             if(!this.disable.lists){
42462                 tb.add(
42463                     '-',
42464                     btn('insertorderedlist'),
42465                     btn('insertunorderedlist')
42466                 );
42467             }
42468             if(!this.disable.sourceEdit){
42469                 tb.add(
42470                     '-',
42471                     btn('sourceedit', true, function(btn){
42472                         this.toggleSourceEdit(btn.pressed);
42473                     })
42474                 );
42475             }
42476         //}
42477         
42478         var smenu = { };
42479         // special menu.. - needs to be tidied up..
42480         if (!this.disable.special) {
42481             smenu = {
42482                 text: "&#169;",
42483                 cls: 'x-edit-none',
42484                 
42485                 menu : {
42486                     items : []
42487                 }
42488             };
42489             for (var i =0; i < this.specialChars.length; i++) {
42490                 smenu.menu.items.push({
42491                     
42492                     html: this.specialChars[i],
42493                     handler: function(a,b) {
42494                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42495                         //editor.insertAtCursor(a.html);
42496                         
42497                     },
42498                     tabIndex:-1
42499                 });
42500             }
42501             
42502             
42503             tb.add(smenu);
42504             
42505             
42506         }
42507         
42508         var cmenu = { };
42509         if (!this.disable.cleanStyles) {
42510             cmenu = {
42511                 cls: 'x-btn-icon x-btn-clear',
42512                 
42513                 menu : {
42514                     items : []
42515                 }
42516             };
42517             for (var i =0; i < this.cleanStyles.length; i++) {
42518                 cmenu.menu.items.push({
42519                     actiontype : this.cleanStyles[i],
42520                     html: 'Remove ' + this.cleanStyles[i],
42521                     handler: function(a,b) {
42522                         Roo.log(a);
42523                         Roo.log(b);
42524                         var c = Roo.get(editor.doc.body);
42525                         c.select('[style]').each(function(s) {
42526                             s.dom.style.removeProperty(a.actiontype);
42527                         });
42528                         
42529                     },
42530                     tabIndex:-1
42531                 });
42532             }
42533             
42534             tb.add(cmenu);
42535         }
42536          
42537         if (!this.disable.specialElements) {
42538             var semenu = {
42539                 text: "Other;",
42540                 cls: 'x-edit-none',
42541                 menu : {
42542                     items : []
42543                 }
42544             };
42545             for (var i =0; i < this.specialElements.length; i++) {
42546                 semenu.menu.items.push(
42547                     Roo.apply({ 
42548                         handler: function(a,b) {
42549                             editor.insertAtCursor(this.ihtml);
42550                         }
42551                     }, this.specialElements[i])
42552                 );
42553                     
42554             }
42555             
42556             tb.add(semenu);
42557             
42558             
42559         }
42560          
42561         
42562         if (this.btns) {
42563             for(var i =0; i< this.btns.length;i++) {
42564                 var b = Roo.factory(this.btns[i],Roo.form);
42565                 b.cls =  'x-edit-none';
42566                 b.scope = editor;
42567                 tb.add(b);
42568             }
42569         
42570         }
42571         
42572         
42573         
42574         // disable everything...
42575         
42576         this.tb.items.each(function(item){
42577            if(item.id != editor.frameId+ '-sourceedit'){
42578                 item.disable();
42579             }
42580         });
42581         this.rendered = true;
42582         
42583         // the all the btns;
42584         editor.on('editorevent', this.updateToolbar, this);
42585         // other toolbars need to implement this..
42586         //editor.on('editmodechange', this.updateToolbar, this);
42587     },
42588     
42589     
42590     
42591     /**
42592      * Protected method that will not generally be called directly. It triggers
42593      * a toolbar update by reading the markup state of the current selection in the editor.
42594      */
42595     updateToolbar: function(){
42596
42597         if(!this.editor.activated){
42598             this.editor.onFirstFocus();
42599             return;
42600         }
42601
42602         var btns = this.tb.items.map, 
42603             doc = this.editor.doc,
42604             frameId = this.editor.frameId;
42605
42606         if(!this.disable.font && !Roo.isSafari){
42607             /*
42608             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42609             if(name != this.fontSelect.dom.value){
42610                 this.fontSelect.dom.value = name;
42611             }
42612             */
42613         }
42614         if(!this.disable.format){
42615             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42616             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42617             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42618         }
42619         if(!this.disable.alignments){
42620             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42621             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42622             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42623         }
42624         if(!Roo.isSafari && !this.disable.lists){
42625             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42626             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42627         }
42628         
42629         var ans = this.editor.getAllAncestors();
42630         if (this.formatCombo) {
42631             
42632             
42633             var store = this.formatCombo.store;
42634             this.formatCombo.setValue("");
42635             for (var i =0; i < ans.length;i++) {
42636                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42637                     // select it..
42638                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42639                     break;
42640                 }
42641             }
42642         }
42643         
42644         
42645         
42646         // hides menus... - so this cant be on a menu...
42647         Roo.menu.MenuMgr.hideAll();
42648
42649         //this.editorsyncValue();
42650     },
42651    
42652     
42653     createFontOptions : function(){
42654         var buf = [], fs = this.fontFamilies, ff, lc;
42655         
42656         
42657         
42658         for(var i = 0, len = fs.length; i< len; i++){
42659             ff = fs[i];
42660             lc = ff.toLowerCase();
42661             buf.push(
42662                 '<option value="',lc,'" style="font-family:',ff,';"',
42663                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42664                     ff,
42665                 '</option>'
42666             );
42667         }
42668         return buf.join('');
42669     },
42670     
42671     toggleSourceEdit : function(sourceEditMode){
42672         if(sourceEditMode === undefined){
42673             sourceEditMode = !this.sourceEditMode;
42674         }
42675         this.sourceEditMode = sourceEditMode === true;
42676         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42677         // just toggle the button?
42678         if(btn.pressed !== this.editor.sourceEditMode){
42679             btn.toggle(this.editor.sourceEditMode);
42680             return;
42681         }
42682         
42683         if(this.sourceEditMode){
42684             this.tb.items.each(function(item){
42685                 if(item.cmd != 'sourceedit'){
42686                     item.disable();
42687                 }
42688             });
42689           
42690         }else{
42691             if(this.initialized){
42692                 this.tb.items.each(function(item){
42693                     item.enable();
42694                 });
42695             }
42696             
42697         }
42698         // tell the editor that it's been pressed..
42699         this.editor.toggleSourceEdit(sourceEditMode);
42700        
42701     },
42702      /**
42703      * Object collection of toolbar tooltips for the buttons in the editor. The key
42704      * is the command id associated with that button and the value is a valid QuickTips object.
42705      * For example:
42706 <pre><code>
42707 {
42708     bold : {
42709         title: 'Bold (Ctrl+B)',
42710         text: 'Make the selected text bold.',
42711         cls: 'x-html-editor-tip'
42712     },
42713     italic : {
42714         title: 'Italic (Ctrl+I)',
42715         text: 'Make the selected text italic.',
42716         cls: 'x-html-editor-tip'
42717     },
42718     ...
42719 </code></pre>
42720     * @type Object
42721      */
42722     buttonTips : {
42723         bold : {
42724             title: 'Bold (Ctrl+B)',
42725             text: 'Make the selected text bold.',
42726             cls: 'x-html-editor-tip'
42727         },
42728         italic : {
42729             title: 'Italic (Ctrl+I)',
42730             text: 'Make the selected text italic.',
42731             cls: 'x-html-editor-tip'
42732         },
42733         underline : {
42734             title: 'Underline (Ctrl+U)',
42735             text: 'Underline the selected text.',
42736             cls: 'x-html-editor-tip'
42737         },
42738         increasefontsize : {
42739             title: 'Grow Text',
42740             text: 'Increase the font size.',
42741             cls: 'x-html-editor-tip'
42742         },
42743         decreasefontsize : {
42744             title: 'Shrink Text',
42745             text: 'Decrease the font size.',
42746             cls: 'x-html-editor-tip'
42747         },
42748         backcolor : {
42749             title: 'Text Highlight Color',
42750             text: 'Change the background color of the selected text.',
42751             cls: 'x-html-editor-tip'
42752         },
42753         forecolor : {
42754             title: 'Font Color',
42755             text: 'Change the color of the selected text.',
42756             cls: 'x-html-editor-tip'
42757         },
42758         justifyleft : {
42759             title: 'Align Text Left',
42760             text: 'Align text to the left.',
42761             cls: 'x-html-editor-tip'
42762         },
42763         justifycenter : {
42764             title: 'Center Text',
42765             text: 'Center text in the editor.',
42766             cls: 'x-html-editor-tip'
42767         },
42768         justifyright : {
42769             title: 'Align Text Right',
42770             text: 'Align text to the right.',
42771             cls: 'x-html-editor-tip'
42772         },
42773         insertunorderedlist : {
42774             title: 'Bullet List',
42775             text: 'Start a bulleted list.',
42776             cls: 'x-html-editor-tip'
42777         },
42778         insertorderedlist : {
42779             title: 'Numbered List',
42780             text: 'Start a numbered list.',
42781             cls: 'x-html-editor-tip'
42782         },
42783         createlink : {
42784             title: 'Hyperlink',
42785             text: 'Make the selected text a hyperlink.',
42786             cls: 'x-html-editor-tip'
42787         },
42788         sourceedit : {
42789             title: 'Source Edit',
42790             text: 'Switch to source editing mode.',
42791             cls: 'x-html-editor-tip'
42792         }
42793     },
42794     // private
42795     onDestroy : function(){
42796         if(this.rendered){
42797             
42798             this.tb.items.each(function(item){
42799                 if(item.menu){
42800                     item.menu.removeAll();
42801                     if(item.menu.el){
42802                         item.menu.el.destroy();
42803                     }
42804                 }
42805                 item.destroy();
42806             });
42807              
42808         }
42809     },
42810     onFirstFocus: function() {
42811         this.tb.items.each(function(item){
42812            item.enable();
42813         });
42814     }
42815 });
42816
42817
42818
42819
42820 // <script type="text/javascript">
42821 /*
42822  * Based on
42823  * Ext JS Library 1.1.1
42824  * Copyright(c) 2006-2007, Ext JS, LLC.
42825  *  
42826  
42827  */
42828
42829  
42830 /**
42831  * @class Roo.form.HtmlEditor.ToolbarContext
42832  * Context Toolbar
42833  * 
42834  * Usage:
42835  *
42836  new Roo.form.HtmlEditor({
42837     ....
42838     toolbars : [
42839         { xtype: 'ToolbarStandard', styles : {} }
42840         { xtype: 'ToolbarContext', disable : {} }
42841     ]
42842 })
42843
42844      
42845  * 
42846  * @config : {Object} disable List of elements to disable.. (not done yet.)
42847  * @config : {Object} styles  Map of styles available.
42848  * 
42849  */
42850
42851 Roo.form.HtmlEditor.ToolbarContext = function(config)
42852 {
42853     
42854     Roo.apply(this, config);
42855     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42856     // dont call parent... till later.
42857     this.styles = this.styles || {};
42858 }
42859
42860  
42861
42862 Roo.form.HtmlEditor.ToolbarContext.types = {
42863     'IMG' : {
42864         width : {
42865             title: "Width",
42866             width: 40
42867         },
42868         height:  {
42869             title: "Height",
42870             width: 40
42871         },
42872         align: {
42873             title: "Align",
42874             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42875             width : 80
42876             
42877         },
42878         border: {
42879             title: "Border",
42880             width: 40
42881         },
42882         alt: {
42883             title: "Alt",
42884             width: 120
42885         },
42886         src : {
42887             title: "Src",
42888             width: 220
42889         }
42890         
42891     },
42892     'A' : {
42893         name : {
42894             title: "Name",
42895             width: 50
42896         },
42897         target:  {
42898             title: "Target",
42899             width: 120
42900         },
42901         href:  {
42902             title: "Href",
42903             width: 220
42904         } // border?
42905         
42906     },
42907     'TABLE' : {
42908         rows : {
42909             title: "Rows",
42910             width: 20
42911         },
42912         cols : {
42913             title: "Cols",
42914             width: 20
42915         },
42916         width : {
42917             title: "Width",
42918             width: 40
42919         },
42920         height : {
42921             title: "Height",
42922             width: 40
42923         },
42924         border : {
42925             title: "Border",
42926             width: 20
42927         }
42928     },
42929     'TD' : {
42930         width : {
42931             title: "Width",
42932             width: 40
42933         },
42934         height : {
42935             title: "Height",
42936             width: 40
42937         },   
42938         align: {
42939             title: "Align",
42940             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42941             width: 80
42942         },
42943         valign: {
42944             title: "Valign",
42945             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42946             width: 80
42947         },
42948         colspan: {
42949             title: "Colspan",
42950             width: 20
42951             
42952         },
42953          'font-family'  : {
42954             title : "Font",
42955             style : 'fontFamily',
42956             displayField: 'display',
42957             optname : 'font-family',
42958             width: 140
42959         }
42960     },
42961     'INPUT' : {
42962         name : {
42963             title: "name",
42964             width: 120
42965         },
42966         value : {
42967             title: "Value",
42968             width: 120
42969         },
42970         width : {
42971             title: "Width",
42972             width: 40
42973         }
42974     },
42975     'LABEL' : {
42976         'for' : {
42977             title: "For",
42978             width: 120
42979         }
42980     },
42981     'TEXTAREA' : {
42982           name : {
42983             title: "name",
42984             width: 120
42985         },
42986         rows : {
42987             title: "Rows",
42988             width: 20
42989         },
42990         cols : {
42991             title: "Cols",
42992             width: 20
42993         }
42994     },
42995     'SELECT' : {
42996         name : {
42997             title: "name",
42998             width: 120
42999         },
43000         selectoptions : {
43001             title: "Options",
43002             width: 200
43003         }
43004     },
43005     
43006     // should we really allow this??
43007     // should this just be 
43008     'BODY' : {
43009         title : {
43010             title: "Title",
43011             width: 200,
43012             disabled : true
43013         }
43014     },
43015     'SPAN' : {
43016         'font-family'  : {
43017             title : "Font",
43018             style : 'fontFamily',
43019             displayField: 'display',
43020             optname : 'font-family',
43021             width: 140
43022         }
43023     },
43024     'DIV' : {
43025         'font-family'  : {
43026             title : "Font",
43027             style : 'fontFamily',
43028             displayField: 'display',
43029             optname : 'font-family',
43030             width: 140
43031         }
43032     },
43033      'P' : {
43034         'font-family'  : {
43035             title : "Font",
43036             style : 'fontFamily',
43037             displayField: 'display',
43038             optname : 'font-family',
43039             width: 140
43040         }
43041     },
43042     
43043     '*' : {
43044         // empty..
43045     }
43046
43047 };
43048
43049 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43050 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43051
43052 Roo.form.HtmlEditor.ToolbarContext.options = {
43053         'font-family'  : [ 
43054                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43055                 [ 'Courier New', 'Courier New'],
43056                 [ 'Tahoma', 'Tahoma'],
43057                 [ 'Times New Roman,serif', 'Times'],
43058                 [ 'Verdana','Verdana' ]
43059         ]
43060 };
43061
43062 // fixme - these need to be configurable..
43063  
43064
43065 Roo.form.HtmlEditor.ToolbarContext.types
43066
43067
43068 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43069     
43070     tb: false,
43071     
43072     rendered: false,
43073     
43074     editor : false,
43075     /**
43076      * @cfg {Object} disable  List of toolbar elements to disable
43077          
43078      */
43079     disable : false,
43080     /**
43081      * @cfg {Object} styles List of styles 
43082      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43083      *
43084      * These must be defined in the page, so they get rendered correctly..
43085      * .headline { }
43086      * TD.underline { }
43087      * 
43088      */
43089     styles : false,
43090     
43091     options: false,
43092     
43093     toolbars : false,
43094     
43095     init : function(editor)
43096     {
43097         this.editor = editor;
43098         
43099         
43100         var fid = editor.frameId;
43101         var etb = this;
43102         function btn(id, toggle, handler){
43103             var xid = fid + '-'+ id ;
43104             return {
43105                 id : xid,
43106                 cmd : id,
43107                 cls : 'x-btn-icon x-edit-'+id,
43108                 enableToggle:toggle !== false,
43109                 scope: editor, // was editor...
43110                 handler:handler||editor.relayBtnCmd,
43111                 clickEvent:'mousedown',
43112                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43113                 tabIndex:-1
43114             };
43115         }
43116         // create a new element.
43117         var wdiv = editor.wrap.createChild({
43118                 tag: 'div'
43119             }, editor.wrap.dom.firstChild.nextSibling, true);
43120         
43121         // can we do this more than once??
43122         
43123          // stop form submits
43124       
43125  
43126         // disable everything...
43127         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43128         this.toolbars = {};
43129            
43130         for (var i in  ty) {
43131           
43132             this.toolbars[i] = this.buildToolbar(ty[i],i);
43133         }
43134         this.tb = this.toolbars.BODY;
43135         this.tb.el.show();
43136         this.buildFooter();
43137         this.footer.show();
43138         editor.on('hide', function( ) { this.footer.hide() }, this);
43139         editor.on('show', function( ) { this.footer.show() }, this);
43140         
43141          
43142         this.rendered = true;
43143         
43144         // the all the btns;
43145         editor.on('editorevent', this.updateToolbar, this);
43146         // other toolbars need to implement this..
43147         //editor.on('editmodechange', this.updateToolbar, this);
43148     },
43149     
43150     
43151     
43152     /**
43153      * Protected method that will not generally be called directly. It triggers
43154      * a toolbar update by reading the markup state of the current selection in the editor.
43155      */
43156     updateToolbar: function(editor,ev,sel){
43157
43158         //Roo.log(ev);
43159         // capture mouse up - this is handy for selecting images..
43160         // perhaps should go somewhere else...
43161         if(!this.editor.activated){
43162              this.editor.onFirstFocus();
43163             return;
43164         }
43165         
43166         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43167         // selectNode - might want to handle IE?
43168         if (ev &&
43169             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43170             ev.target && ev.target.tagName == 'IMG') {
43171             // they have click on an image...
43172             // let's see if we can change the selection...
43173             sel = ev.target;
43174          
43175               var nodeRange = sel.ownerDocument.createRange();
43176             try {
43177                 nodeRange.selectNode(sel);
43178             } catch (e) {
43179                 nodeRange.selectNodeContents(sel);
43180             }
43181             //nodeRange.collapse(true);
43182             var s = editor.win.getSelection();
43183             s.removeAllRanges();
43184             s.addRange(nodeRange);
43185         }  
43186         
43187       
43188         var updateFooter = sel ? false : true;
43189         
43190         
43191         var ans = this.editor.getAllAncestors();
43192         
43193         // pick
43194         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43195         
43196         if (!sel) { 
43197             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43198             sel = sel ? sel : this.editor.doc.body;
43199             sel = sel.tagName.length ? sel : this.editor.doc.body;
43200             
43201         }
43202         // pick a menu that exists..
43203         var tn = sel.tagName.toUpperCase();
43204         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43205         
43206         tn = sel.tagName.toUpperCase();
43207         
43208         var lastSel = this.tb.selectedNode
43209         
43210         this.tb.selectedNode = sel;
43211         
43212         // if current menu does not match..
43213         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43214                 
43215             this.tb.el.hide();
43216             ///console.log("show: " + tn);
43217             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43218             this.tb.el.show();
43219             // update name
43220             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43221             
43222             
43223             // update attributes
43224             if (this.tb.fields) {
43225                 this.tb.fields.each(function(e) {
43226                     if (e.stylename) {
43227                         e.setValue(sel.style[e.stylename]);
43228                         return;
43229                     } 
43230                    e.setValue(sel.getAttribute(e.attrname));
43231                 });
43232             }
43233             
43234             var hasStyles = false;
43235             for(var i in this.styles) {
43236                 hasStyles = true;
43237                 break;
43238             }
43239             
43240             // update styles
43241             if (hasStyles) { 
43242                 var st = this.tb.fields.item(0);
43243                 
43244                 st.store.removeAll();
43245                
43246                 
43247                 var cn = sel.className.split(/\s+/);
43248                 
43249                 var avs = [];
43250                 if (this.styles['*']) {
43251                     
43252                     Roo.each(this.styles['*'], function(v) {
43253                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43254                     });
43255                 }
43256                 if (this.styles[tn]) { 
43257                     Roo.each(this.styles[tn], function(v) {
43258                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43259                     });
43260                 }
43261                 
43262                 st.store.loadData(avs);
43263                 st.collapse();
43264                 st.setValue(cn);
43265             }
43266             // flag our selected Node.
43267             this.tb.selectedNode = sel;
43268            
43269            
43270             Roo.menu.MenuMgr.hideAll();
43271
43272         }
43273         
43274         if (!updateFooter) {
43275             //this.footDisp.dom.innerHTML = ''; 
43276             return;
43277         }
43278         // update the footer
43279         //
43280         var html = '';
43281         
43282         this.footerEls = ans.reverse();
43283         Roo.each(this.footerEls, function(a,i) {
43284             if (!a) { return; }
43285             html += html.length ? ' &gt; '  :  '';
43286             
43287             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43288             
43289         });
43290        
43291         // 
43292         var sz = this.footDisp.up('td').getSize();
43293         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43294         this.footDisp.dom.style.marginLeft = '5px';
43295         
43296         this.footDisp.dom.style.overflow = 'hidden';
43297         
43298         this.footDisp.dom.innerHTML = html;
43299             
43300         //this.editorsyncValue();
43301     },
43302      
43303     
43304    
43305        
43306     // private
43307     onDestroy : function(){
43308         if(this.rendered){
43309             
43310             this.tb.items.each(function(item){
43311                 if(item.menu){
43312                     item.menu.removeAll();
43313                     if(item.menu.el){
43314                         item.menu.el.destroy();
43315                     }
43316                 }
43317                 item.destroy();
43318             });
43319              
43320         }
43321     },
43322     onFirstFocus: function() {
43323         // need to do this for all the toolbars..
43324         this.tb.items.each(function(item){
43325            item.enable();
43326         });
43327     },
43328     buildToolbar: function(tlist, nm)
43329     {
43330         var editor = this.editor;
43331          // create a new element.
43332         var wdiv = editor.wrap.createChild({
43333                 tag: 'div'
43334             }, editor.wrap.dom.firstChild.nextSibling, true);
43335         
43336        
43337         var tb = new Roo.Toolbar(wdiv);
43338         // add the name..
43339         
43340         tb.add(nm+ ":&nbsp;");
43341         
43342         var styles = [];
43343         for(var i in this.styles) {
43344             styles.push(i);
43345         }
43346         
43347         // styles...
43348         if (styles && styles.length) {
43349             
43350             // this needs a multi-select checkbox...
43351             tb.addField( new Roo.form.ComboBox({
43352                 store: new Roo.data.SimpleStore({
43353                     id : 'val',
43354                     fields: ['val', 'selected'],
43355                     data : [] 
43356                 }),
43357                 name : '-roo-edit-className',
43358                 attrname : 'className',
43359                 displayField: 'val',
43360                 typeAhead: false,
43361                 mode: 'local',
43362                 editable : false,
43363                 triggerAction: 'all',
43364                 emptyText:'Select Style',
43365                 selectOnFocus:true,
43366                 width: 130,
43367                 listeners : {
43368                     'select': function(c, r, i) {
43369                         // initial support only for on class per el..
43370                         tb.selectedNode.className =  r ? r.get('val') : '';
43371                         editor.syncValue();
43372                     }
43373                 }
43374     
43375             }));
43376         }
43377         
43378         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43379         var tbops = tbc.options;
43380         
43381         for (var i in tlist) {
43382             
43383             var item = tlist[i];
43384             tb.add(item.title + ":&nbsp;");
43385             
43386             
43387             //optname == used so you can configure the options available..
43388             var opts = item.opts ? item.opts : false;
43389             if (item.optname) {
43390                 opts = tbops[item.optname];
43391            
43392             }
43393             
43394             if (opts) {
43395                 // opts == pulldown..
43396                 tb.addField( new Roo.form.ComboBox({
43397                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43398                         id : 'val',
43399                         fields: ['val', 'display'],
43400                         data : opts  
43401                     }),
43402                     name : '-roo-edit-' + i,
43403                     attrname : i,
43404                     stylename : item.style ? item.style : false,
43405                     displayField: item.displayField ? item.displayField : 'val',
43406                     valueField :  'val',
43407                     typeAhead: false,
43408                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43409                     editable : false,
43410                     triggerAction: 'all',
43411                     emptyText:'Select',
43412                     selectOnFocus:true,
43413                     width: item.width ? item.width  : 130,
43414                     listeners : {
43415                         'select': function(c, r, i) {
43416                             if (c.stylename) {
43417                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43418                                 return;
43419                             }
43420                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43421                         }
43422                     }
43423
43424                 }));
43425                 continue;
43426                     
43427                  
43428                 
43429                 tb.addField( new Roo.form.TextField({
43430                     name: i,
43431                     width: 100,
43432                     //allowBlank:false,
43433                     value: ''
43434                 }));
43435                 continue;
43436             }
43437             tb.addField( new Roo.form.TextField({
43438                 name: '-roo-edit-' + i,
43439                 attrname : i,
43440                 
43441                 width: item.width,
43442                 //allowBlank:true,
43443                 value: '',
43444                 listeners: {
43445                     'change' : function(f, nv, ov) {
43446                         tb.selectedNode.setAttribute(f.attrname, nv);
43447                     }
43448                 }
43449             }));
43450              
43451         }
43452         tb.addFill();
43453         var _this = this;
43454         tb.addButton( {
43455             text: 'Remove Tag',
43456     
43457             listeners : {
43458                 click : function ()
43459                 {
43460                     // remove
43461                     // undo does not work.
43462                      
43463                     var sn = tb.selectedNode;
43464                     
43465                     var pn = sn.parentNode;
43466                     
43467                     var stn =  sn.childNodes[0];
43468                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43469                     while (sn.childNodes.length) {
43470                         var node = sn.childNodes[0];
43471                         sn.removeChild(node);
43472                         //Roo.log(node);
43473                         pn.insertBefore(node, sn);
43474                         
43475                     }
43476                     pn.removeChild(sn);
43477                     var range = editor.createRange();
43478         
43479                     range.setStart(stn,0);
43480                     range.setEnd(en,0); //????
43481                     //range.selectNode(sel);
43482                     
43483                     
43484                     var selection = editor.getSelection();
43485                     selection.removeAllRanges();
43486                     selection.addRange(range);
43487                     
43488                     
43489                     
43490                     //_this.updateToolbar(null, null, pn);
43491                     _this.updateToolbar(null, null, null);
43492                     _this.footDisp.dom.innerHTML = ''; 
43493                 }
43494             }
43495             
43496                     
43497                 
43498             
43499         });
43500         
43501         
43502         tb.el.on('click', function(e){
43503             e.preventDefault(); // what does this do?
43504         });
43505         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43506         tb.el.hide();
43507         tb.name = nm;
43508         // dont need to disable them... as they will get hidden
43509         return tb;
43510          
43511         
43512     },
43513     buildFooter : function()
43514     {
43515         
43516         var fel = this.editor.wrap.createChild();
43517         this.footer = new Roo.Toolbar(fel);
43518         // toolbar has scrolly on left / right?
43519         var footDisp= new Roo.Toolbar.Fill();
43520         var _t = this;
43521         this.footer.add(
43522             {
43523                 text : '&lt;',
43524                 xtype: 'Button',
43525                 handler : function() {
43526                     _t.footDisp.scrollTo('left',0,true)
43527                 }
43528             }
43529         );
43530         this.footer.add( footDisp );
43531         this.footer.add( 
43532             {
43533                 text : '&gt;',
43534                 xtype: 'Button',
43535                 handler : function() {
43536                     // no animation..
43537                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43538                 }
43539             }
43540         );
43541         var fel = Roo.get(footDisp.el);
43542         fel.addClass('x-editor-context');
43543         this.footDispWrap = fel; 
43544         this.footDispWrap.overflow  = 'hidden';
43545         
43546         this.footDisp = fel.createChild();
43547         this.footDispWrap.on('click', this.onContextClick, this)
43548         
43549         
43550     },
43551     onContextClick : function (ev,dom)
43552     {
43553         ev.preventDefault();
43554         var  cn = dom.className;
43555         //Roo.log(cn);
43556         if (!cn.match(/x-ed-loc-/)) {
43557             return;
43558         }
43559         var n = cn.split('-').pop();
43560         var ans = this.footerEls;
43561         var sel = ans[n];
43562         
43563          // pick
43564         var range = this.editor.createRange();
43565         
43566         range.selectNodeContents(sel);
43567         //range.selectNode(sel);
43568         
43569         
43570         var selection = this.editor.getSelection();
43571         selection.removeAllRanges();
43572         selection.addRange(range);
43573         
43574         
43575         
43576         this.updateToolbar(null, null, sel);
43577         
43578         
43579     }
43580     
43581     
43582     
43583     
43584     
43585 });
43586
43587
43588
43589
43590
43591 /*
43592  * Based on:
43593  * Ext JS Library 1.1.1
43594  * Copyright(c) 2006-2007, Ext JS, LLC.
43595  *
43596  * Originally Released Under LGPL - original licence link has changed is not relivant.
43597  *
43598  * Fork - LGPL
43599  * <script type="text/javascript">
43600  */
43601  
43602 /**
43603  * @class Roo.form.BasicForm
43604  * @extends Roo.util.Observable
43605  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43606  * @constructor
43607  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43608  * @param {Object} config Configuration options
43609  */
43610 Roo.form.BasicForm = function(el, config){
43611     this.allItems = [];
43612     this.childForms = [];
43613     Roo.apply(this, config);
43614     /*
43615      * The Roo.form.Field items in this form.
43616      * @type MixedCollection
43617      */
43618      
43619      
43620     this.items = new Roo.util.MixedCollection(false, function(o){
43621         return o.id || (o.id = Roo.id());
43622     });
43623     this.addEvents({
43624         /**
43625          * @event beforeaction
43626          * Fires before any action is performed. Return false to cancel the action.
43627          * @param {Form} this
43628          * @param {Action} action The action to be performed
43629          */
43630         beforeaction: true,
43631         /**
43632          * @event actionfailed
43633          * Fires when an action fails.
43634          * @param {Form} this
43635          * @param {Action} action The action that failed
43636          */
43637         actionfailed : true,
43638         /**
43639          * @event actioncomplete
43640          * Fires when an action is completed.
43641          * @param {Form} this
43642          * @param {Action} action The action that completed
43643          */
43644         actioncomplete : true
43645     });
43646     if(el){
43647         this.initEl(el);
43648     }
43649     Roo.form.BasicForm.superclass.constructor.call(this);
43650 };
43651
43652 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43653     /**
43654      * @cfg {String} method
43655      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43656      */
43657     /**
43658      * @cfg {DataReader} reader
43659      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43660      * This is optional as there is built-in support for processing JSON.
43661      */
43662     /**
43663      * @cfg {DataReader} errorReader
43664      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43665      * This is completely optional as there is built-in support for processing JSON.
43666      */
43667     /**
43668      * @cfg {String} url
43669      * The URL to use for form actions if one isn't supplied in the action options.
43670      */
43671     /**
43672      * @cfg {Boolean} fileUpload
43673      * Set to true if this form is a file upload.
43674      */
43675      
43676     /**
43677      * @cfg {Object} baseParams
43678      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43679      */
43680      /**
43681      
43682     /**
43683      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43684      */
43685     timeout: 30,
43686
43687     // private
43688     activeAction : null,
43689
43690     /**
43691      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43692      * or setValues() data instead of when the form was first created.
43693      */
43694     trackResetOnLoad : false,
43695     
43696     
43697     /**
43698      * childForms - used for multi-tab forms
43699      * @type {Array}
43700      */
43701     childForms : false,
43702     
43703     /**
43704      * allItems - full list of fields.
43705      * @type {Array}
43706      */
43707     allItems : false,
43708     
43709     /**
43710      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43711      * element by passing it or its id or mask the form itself by passing in true.
43712      * @type Mixed
43713      */
43714     waitMsgTarget : false,
43715
43716     // private
43717     initEl : function(el){
43718         this.el = Roo.get(el);
43719         this.id = this.el.id || Roo.id();
43720         this.el.on('submit', this.onSubmit, this);
43721         this.el.addClass('x-form');
43722     },
43723
43724     // private
43725     onSubmit : function(e){
43726         e.stopEvent();
43727     },
43728
43729     /**
43730      * Returns true if client-side validation on the form is successful.
43731      * @return Boolean
43732      */
43733     isValid : function(){
43734         var valid = true;
43735         this.items.each(function(f){
43736            if(!f.validate()){
43737                valid = false;
43738            }
43739         });
43740         return valid;
43741     },
43742
43743     /**
43744      * Returns true if any fields in this form have changed since their original load.
43745      * @return Boolean
43746      */
43747     isDirty : function(){
43748         var dirty = false;
43749         this.items.each(function(f){
43750            if(f.isDirty()){
43751                dirty = true;
43752                return false;
43753            }
43754         });
43755         return dirty;
43756     },
43757
43758     /**
43759      * Performs a predefined action (submit or load) or custom actions you define on this form.
43760      * @param {String} actionName The name of the action type
43761      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43762      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43763      * accept other config options):
43764      * <pre>
43765 Property          Type             Description
43766 ----------------  ---------------  ----------------------------------------------------------------------------------
43767 url               String           The url for the action (defaults to the form's url)
43768 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43769 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43770 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43771                                    validate the form on the client (defaults to false)
43772      * </pre>
43773      * @return {BasicForm} this
43774      */
43775     doAction : function(action, options){
43776         if(typeof action == 'string'){
43777             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43778         }
43779         if(this.fireEvent('beforeaction', this, action) !== false){
43780             this.beforeAction(action);
43781             action.run.defer(100, action);
43782         }
43783         return this;
43784     },
43785
43786     /**
43787      * Shortcut to do a submit action.
43788      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43789      * @return {BasicForm} this
43790      */
43791     submit : function(options){
43792         this.doAction('submit', options);
43793         return this;
43794     },
43795
43796     /**
43797      * Shortcut to do a load action.
43798      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43799      * @return {BasicForm} this
43800      */
43801     load : function(options){
43802         this.doAction('load', options);
43803         return this;
43804     },
43805
43806     /**
43807      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43808      * @param {Record} record The record to edit
43809      * @return {BasicForm} this
43810      */
43811     updateRecord : function(record){
43812         record.beginEdit();
43813         var fs = record.fields;
43814         fs.each(function(f){
43815             var field = this.findField(f.name);
43816             if(field){
43817                 record.set(f.name, field.getValue());
43818             }
43819         }, this);
43820         record.endEdit();
43821         return this;
43822     },
43823
43824     /**
43825      * Loads an Roo.data.Record into this form.
43826      * @param {Record} record The record to load
43827      * @return {BasicForm} this
43828      */
43829     loadRecord : function(record){
43830         this.setValues(record.data);
43831         return this;
43832     },
43833
43834     // private
43835     beforeAction : function(action){
43836         var o = action.options;
43837         
43838        
43839         if(this.waitMsgTarget === true){
43840             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43841         }else if(this.waitMsgTarget){
43842             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43843             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43844         }else {
43845             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43846         }
43847          
43848     },
43849
43850     // private
43851     afterAction : function(action, success){
43852         this.activeAction = null;
43853         var o = action.options;
43854         
43855         if(this.waitMsgTarget === true){
43856             this.el.unmask();
43857         }else if(this.waitMsgTarget){
43858             this.waitMsgTarget.unmask();
43859         }else{
43860             Roo.MessageBox.updateProgress(1);
43861             Roo.MessageBox.hide();
43862         }
43863          
43864         if(success){
43865             if(o.reset){
43866                 this.reset();
43867             }
43868             Roo.callback(o.success, o.scope, [this, action]);
43869             this.fireEvent('actioncomplete', this, action);
43870             
43871         }else{
43872             
43873             // failure condition..
43874             // we have a scenario where updates need confirming.
43875             // eg. if a locking scenario exists..
43876             // we look for { errors : { needs_confirm : true }} in the response.
43877             if (
43878                 (typeof(action.result) != 'undefined')  &&
43879                 (typeof(action.result.errors) != 'undefined')  &&
43880                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43881            ){
43882                 var _t = this;
43883                 Roo.MessageBox.confirm(
43884                     "Change requires confirmation",
43885                     action.result.errorMsg,
43886                     function(r) {
43887                         if (r != 'yes') {
43888                             return;
43889                         }
43890                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43891                     }
43892                     
43893                 );
43894                 
43895                 
43896                 
43897                 return;
43898             }
43899             
43900             Roo.callback(o.failure, o.scope, [this, action]);
43901             // show an error message if no failed handler is set..
43902             if (!this.hasListener('actionfailed')) {
43903                 Roo.MessageBox.alert("Error",
43904                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43905                         action.result.errorMsg :
43906                         "Saving Failed, please check your entries or try again"
43907                 );
43908             }
43909             
43910             this.fireEvent('actionfailed', this, action);
43911         }
43912         
43913     },
43914
43915     /**
43916      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43917      * @param {String} id The value to search for
43918      * @return Field
43919      */
43920     findField : function(id){
43921         var field = this.items.get(id);
43922         if(!field){
43923             this.items.each(function(f){
43924                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43925                     field = f;
43926                     return false;
43927                 }
43928             });
43929         }
43930         return field || null;
43931     },
43932
43933     /**
43934      * Add a secondary form to this one, 
43935      * Used to provide tabbed forms. One form is primary, with hidden values 
43936      * which mirror the elements from the other forms.
43937      * 
43938      * @param {Roo.form.Form} form to add.
43939      * 
43940      */
43941     addForm : function(form)
43942     {
43943        
43944         if (this.childForms.indexOf(form) > -1) {
43945             // already added..
43946             return;
43947         }
43948         this.childForms.push(form);
43949         var n = '';
43950         Roo.each(form.allItems, function (fe) {
43951             
43952             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43953             if (this.findField(n)) { // already added..
43954                 return;
43955             }
43956             var add = new Roo.form.Hidden({
43957                 name : n
43958             });
43959             add.render(this.el);
43960             
43961             this.add( add );
43962         }, this);
43963         
43964     },
43965     /**
43966      * Mark fields in this form invalid in bulk.
43967      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43968      * @return {BasicForm} this
43969      */
43970     markInvalid : function(errors){
43971         if(errors instanceof Array){
43972             for(var i = 0, len = errors.length; i < len; i++){
43973                 var fieldError = errors[i];
43974                 var f = this.findField(fieldError.id);
43975                 if(f){
43976                     f.markInvalid(fieldError.msg);
43977                 }
43978             }
43979         }else{
43980             var field, id;
43981             for(id in errors){
43982                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43983                     field.markInvalid(errors[id]);
43984                 }
43985             }
43986         }
43987         Roo.each(this.childForms || [], function (f) {
43988             f.markInvalid(errors);
43989         });
43990         
43991         return this;
43992     },
43993
43994     /**
43995      * Set values for fields in this form in bulk.
43996      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43997      * @return {BasicForm} this
43998      */
43999     setValues : function(values){
44000         if(values instanceof Array){ // array of objects
44001             for(var i = 0, len = values.length; i < len; i++){
44002                 var v = values[i];
44003                 var f = this.findField(v.id);
44004                 if(f){
44005                     f.setValue(v.value);
44006                     if(this.trackResetOnLoad){
44007                         f.originalValue = f.getValue();
44008                     }
44009                 }
44010             }
44011         }else{ // object hash
44012             var field, id;
44013             for(id in values){
44014                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44015                     
44016                     if (field.setFromData && 
44017                         field.valueField && 
44018                         field.displayField &&
44019                         // combos' with local stores can 
44020                         // be queried via setValue()
44021                         // to set their value..
44022                         (field.store && !field.store.isLocal)
44023                         ) {
44024                         // it's a combo
44025                         var sd = { };
44026                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44027                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44028                         field.setFromData(sd);
44029                         
44030                     } else {
44031                         field.setValue(values[id]);
44032                     }
44033                     
44034                     
44035                     if(this.trackResetOnLoad){
44036                         field.originalValue = field.getValue();
44037                     }
44038                 }
44039             }
44040         }
44041          
44042         Roo.each(this.childForms || [], function (f) {
44043             f.setValues(values);
44044         });
44045                 
44046         return this;
44047     },
44048
44049     /**
44050      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44051      * they are returned as an array.
44052      * @param {Boolean} asString
44053      * @return {Object}
44054      */
44055     getValues : function(asString){
44056         if (this.childForms) {
44057             // copy values from the child forms
44058             Roo.each(this.childForms, function (f) {
44059                 this.setValues(f.getValues());
44060             }, this);
44061         }
44062         
44063         
44064         
44065         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44066         if(asString === true){
44067             return fs;
44068         }
44069         return Roo.urlDecode(fs);
44070     },
44071     
44072     /**
44073      * Returns the fields in this form as an object with key/value pairs. 
44074      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44075      * @return {Object}
44076      */
44077     getFieldValues : function(with_hidden)
44078     {
44079         if (this.childForms) {
44080             // copy values from the child forms
44081             // should this call getFieldValues - probably not as we do not currently copy
44082             // hidden fields when we generate..
44083             Roo.each(this.childForms, function (f) {
44084                 this.setValues(f.getValues());
44085             }, this);
44086         }
44087         
44088         var ret = {};
44089         this.items.each(function(f){
44090             if (!f.getName()) {
44091                 return;
44092             }
44093             var v = f.getValue();
44094             if (f.inputType =='radio') {
44095                 if (typeof(ret[f.getName()]) == 'undefined') {
44096                     ret[f.getName()] = ''; // empty..
44097                 }
44098                 
44099                 if (!f.el.dom.checked) {
44100                     return;
44101                     
44102                 }
44103                 v = f.el.dom.value;
44104                 
44105             }
44106             
44107             // not sure if this supported any more..
44108             if ((typeof(v) == 'object') && f.getRawValue) {
44109                 v = f.getRawValue() ; // dates..
44110             }
44111             // combo boxes where name != hiddenName...
44112             if (f.name != f.getName()) {
44113                 ret[f.name] = f.getRawValue();
44114             }
44115             ret[f.getName()] = v;
44116         });
44117         
44118         return ret;
44119     },
44120
44121     /**
44122      * Clears all invalid messages in this form.
44123      * @return {BasicForm} this
44124      */
44125     clearInvalid : function(){
44126         this.items.each(function(f){
44127            f.clearInvalid();
44128         });
44129         
44130         Roo.each(this.childForms || [], function (f) {
44131             f.clearInvalid();
44132         });
44133         
44134         
44135         return this;
44136     },
44137
44138     /**
44139      * Resets this form.
44140      * @return {BasicForm} this
44141      */
44142     reset : function(){
44143         this.items.each(function(f){
44144             f.reset();
44145         });
44146         
44147         Roo.each(this.childForms || [], function (f) {
44148             f.reset();
44149         });
44150        
44151         
44152         return this;
44153     },
44154
44155     /**
44156      * Add Roo.form components to this form.
44157      * @param {Field} field1
44158      * @param {Field} field2 (optional)
44159      * @param {Field} etc (optional)
44160      * @return {BasicForm} this
44161      */
44162     add : function(){
44163         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44164         return this;
44165     },
44166
44167
44168     /**
44169      * Removes a field from the items collection (does NOT remove its markup).
44170      * @param {Field} field
44171      * @return {BasicForm} this
44172      */
44173     remove : function(field){
44174         this.items.remove(field);
44175         return this;
44176     },
44177
44178     /**
44179      * Looks at the fields in this form, checks them for an id attribute,
44180      * and calls applyTo on the existing dom element with that id.
44181      * @return {BasicForm} this
44182      */
44183     render : function(){
44184         this.items.each(function(f){
44185             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44186                 f.applyTo(f.id);
44187             }
44188         });
44189         return this;
44190     },
44191
44192     /**
44193      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44194      * @param {Object} values
44195      * @return {BasicForm} this
44196      */
44197     applyToFields : function(o){
44198         this.items.each(function(f){
44199            Roo.apply(f, o);
44200         });
44201         return this;
44202     },
44203
44204     /**
44205      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44206      * @param {Object} values
44207      * @return {BasicForm} this
44208      */
44209     applyIfToFields : function(o){
44210         this.items.each(function(f){
44211            Roo.applyIf(f, o);
44212         });
44213         return this;
44214     }
44215 });
44216
44217 // back compat
44218 Roo.BasicForm = Roo.form.BasicForm;/*
44219  * Based on:
44220  * Ext JS Library 1.1.1
44221  * Copyright(c) 2006-2007, Ext JS, LLC.
44222  *
44223  * Originally Released Under LGPL - original licence link has changed is not relivant.
44224  *
44225  * Fork - LGPL
44226  * <script type="text/javascript">
44227  */
44228
44229 /**
44230  * @class Roo.form.Form
44231  * @extends Roo.form.BasicForm
44232  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44233  * @constructor
44234  * @param {Object} config Configuration options
44235  */
44236 Roo.form.Form = function(config){
44237     var xitems =  [];
44238     if (config.items) {
44239         xitems = config.items;
44240         delete config.items;
44241     }
44242    
44243     
44244     Roo.form.Form.superclass.constructor.call(this, null, config);
44245     this.url = this.url || this.action;
44246     if(!this.root){
44247         this.root = new Roo.form.Layout(Roo.applyIf({
44248             id: Roo.id()
44249         }, config));
44250     }
44251     this.active = this.root;
44252     /**
44253      * Array of all the buttons that have been added to this form via {@link addButton}
44254      * @type Array
44255      */
44256     this.buttons = [];
44257     this.allItems = [];
44258     this.addEvents({
44259         /**
44260          * @event clientvalidation
44261          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44262          * @param {Form} this
44263          * @param {Boolean} valid true if the form has passed client-side validation
44264          */
44265         clientvalidation: true,
44266         /**
44267          * @event rendered
44268          * Fires when the form is rendered
44269          * @param {Roo.form.Form} form
44270          */
44271         rendered : true
44272     });
44273     
44274     if (this.progressUrl) {
44275             // push a hidden field onto the list of fields..
44276             this.addxtype( {
44277                     xns: Roo.form, 
44278                     xtype : 'Hidden', 
44279                     name : 'UPLOAD_IDENTIFIER' 
44280             });
44281         }
44282         
44283     
44284     Roo.each(xitems, this.addxtype, this);
44285     
44286     
44287     
44288 };
44289
44290 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44291     /**
44292      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44293      */
44294     /**
44295      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44296      */
44297     /**
44298      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44299      */
44300     buttonAlign:'center',
44301
44302     /**
44303      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44304      */
44305     minButtonWidth:75,
44306
44307     /**
44308      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44309      * This property cascades to child containers if not set.
44310      */
44311     labelAlign:'left',
44312
44313     /**
44314      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44315      * fires a looping event with that state. This is required to bind buttons to the valid
44316      * state using the config value formBind:true on the button.
44317      */
44318     monitorValid : false,
44319
44320     /**
44321      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44322      */
44323     monitorPoll : 200,
44324     
44325     /**
44326      * @cfg {String} progressUrl - Url to return progress data 
44327      */
44328     
44329     progressUrl : false,
44330   
44331     /**
44332      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44333      * fields are added and the column is closed. If no fields are passed the column remains open
44334      * until end() is called.
44335      * @param {Object} config The config to pass to the column
44336      * @param {Field} field1 (optional)
44337      * @param {Field} field2 (optional)
44338      * @param {Field} etc (optional)
44339      * @return Column The column container object
44340      */
44341     column : function(c){
44342         var col = new Roo.form.Column(c);
44343         this.start(col);
44344         if(arguments.length > 1){ // duplicate code required because of Opera
44345             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44346             this.end();
44347         }
44348         return col;
44349     },
44350
44351     /**
44352      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44353      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44354      * until end() is called.
44355      * @param {Object} config The config to pass to the fieldset
44356      * @param {Field} field1 (optional)
44357      * @param {Field} field2 (optional)
44358      * @param {Field} etc (optional)
44359      * @return FieldSet The fieldset container object
44360      */
44361     fieldset : function(c){
44362         var fs = new Roo.form.FieldSet(c);
44363         this.start(fs);
44364         if(arguments.length > 1){ // duplicate code required because of Opera
44365             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44366             this.end();
44367         }
44368         return fs;
44369     },
44370
44371     /**
44372      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44373      * fields are added and the container is closed. If no fields are passed the container remains open
44374      * until end() is called.
44375      * @param {Object} config The config to pass to the Layout
44376      * @param {Field} field1 (optional)
44377      * @param {Field} field2 (optional)
44378      * @param {Field} etc (optional)
44379      * @return Layout The container object
44380      */
44381     container : function(c){
44382         var l = new Roo.form.Layout(c);
44383         this.start(l);
44384         if(arguments.length > 1){ // duplicate code required because of Opera
44385             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44386             this.end();
44387         }
44388         return l;
44389     },
44390
44391     /**
44392      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44393      * @param {Object} container A Roo.form.Layout or subclass of Layout
44394      * @return {Form} this
44395      */
44396     start : function(c){
44397         // cascade label info
44398         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44399         this.active.stack.push(c);
44400         c.ownerCt = this.active;
44401         this.active = c;
44402         return this;
44403     },
44404
44405     /**
44406      * Closes the current open container
44407      * @return {Form} this
44408      */
44409     end : function(){
44410         if(this.active == this.root){
44411             return this;
44412         }
44413         this.active = this.active.ownerCt;
44414         return this;
44415     },
44416
44417     /**
44418      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44419      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44420      * as the label of the field.
44421      * @param {Field} field1
44422      * @param {Field} field2 (optional)
44423      * @param {Field} etc. (optional)
44424      * @return {Form} this
44425      */
44426     add : function(){
44427         this.active.stack.push.apply(this.active.stack, arguments);
44428         this.allItems.push.apply(this.allItems,arguments);
44429         var r = [];
44430         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44431             if(a[i].isFormField){
44432                 r.push(a[i]);
44433             }
44434         }
44435         if(r.length > 0){
44436             Roo.form.Form.superclass.add.apply(this, r);
44437         }
44438         return this;
44439     },
44440     
44441
44442     
44443     
44444     
44445      /**
44446      * Find any element that has been added to a form, using it's ID or name
44447      * This can include framesets, columns etc. along with regular fields..
44448      * @param {String} id - id or name to find.
44449      
44450      * @return {Element} e - or false if nothing found.
44451      */
44452     findbyId : function(id)
44453     {
44454         var ret = false;
44455         if (!id) {
44456             return ret;
44457         }
44458         Roo.each(this.allItems, function(f){
44459             if (f.id == id || f.name == id ){
44460                 ret = f;
44461                 return false;
44462             }
44463         });
44464         return ret;
44465     },
44466
44467     
44468     
44469     /**
44470      * Render this form into the passed container. This should only be called once!
44471      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44472      * @return {Form} this
44473      */
44474     render : function(ct)
44475     {
44476         
44477         
44478         
44479         ct = Roo.get(ct);
44480         var o = this.autoCreate || {
44481             tag: 'form',
44482             method : this.method || 'POST',
44483             id : this.id || Roo.id()
44484         };
44485         this.initEl(ct.createChild(o));
44486
44487         this.root.render(this.el);
44488         
44489        
44490              
44491         this.items.each(function(f){
44492             f.render('x-form-el-'+f.id);
44493         });
44494
44495         if(this.buttons.length > 0){
44496             // tables are required to maintain order and for correct IE layout
44497             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44498                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44499                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44500             }}, null, true);
44501             var tr = tb.getElementsByTagName('tr')[0];
44502             for(var i = 0, len = this.buttons.length; i < len; i++) {
44503                 var b = this.buttons[i];
44504                 var td = document.createElement('td');
44505                 td.className = 'x-form-btn-td';
44506                 b.render(tr.appendChild(td));
44507             }
44508         }
44509         if(this.monitorValid){ // initialize after render
44510             this.startMonitoring();
44511         }
44512         this.fireEvent('rendered', this);
44513         return this;
44514     },
44515
44516     /**
44517      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44518      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44519      * object or a valid Roo.DomHelper element config
44520      * @param {Function} handler The function called when the button is clicked
44521      * @param {Object} scope (optional) The scope of the handler function
44522      * @return {Roo.Button}
44523      */
44524     addButton : function(config, handler, scope){
44525         var bc = {
44526             handler: handler,
44527             scope: scope,
44528             minWidth: this.minButtonWidth,
44529             hideParent:true
44530         };
44531         if(typeof config == "string"){
44532             bc.text = config;
44533         }else{
44534             Roo.apply(bc, config);
44535         }
44536         var btn = new Roo.Button(null, bc);
44537         this.buttons.push(btn);
44538         return btn;
44539     },
44540
44541      /**
44542      * Adds a series of form elements (using the xtype property as the factory method.
44543      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44544      * @param {Object} config 
44545      */
44546     
44547     addxtype : function()
44548     {
44549         var ar = Array.prototype.slice.call(arguments, 0);
44550         var ret = false;
44551         for(var i = 0; i < ar.length; i++) {
44552             if (!ar[i]) {
44553                 continue; // skip -- if this happends something invalid got sent, we 
44554                 // should ignore it, as basically that interface element will not show up
44555                 // and that should be pretty obvious!!
44556             }
44557             
44558             if (Roo.form[ar[i].xtype]) {
44559                 ar[i].form = this;
44560                 var fe = Roo.factory(ar[i], Roo.form);
44561                 if (!ret) {
44562                     ret = fe;
44563                 }
44564                 fe.form = this;
44565                 if (fe.store) {
44566                     fe.store.form = this;
44567                 }
44568                 if (fe.isLayout) {  
44569                          
44570                     this.start(fe);
44571                     this.allItems.push(fe);
44572                     if (fe.items && fe.addxtype) {
44573                         fe.addxtype.apply(fe, fe.items);
44574                         delete fe.items;
44575                     }
44576                      this.end();
44577                     continue;
44578                 }
44579                 
44580                 
44581                  
44582                 this.add(fe);
44583               //  console.log('adding ' + ar[i].xtype);
44584             }
44585             if (ar[i].xtype == 'Button') {  
44586                 //console.log('adding button');
44587                 //console.log(ar[i]);
44588                 this.addButton(ar[i]);
44589                 this.allItems.push(fe);
44590                 continue;
44591             }
44592             
44593             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44594                 alert('end is not supported on xtype any more, use items');
44595             //    this.end();
44596             //    //console.log('adding end');
44597             }
44598             
44599         }
44600         return ret;
44601     },
44602     
44603     /**
44604      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44605      * option "monitorValid"
44606      */
44607     startMonitoring : function(){
44608         if(!this.bound){
44609             this.bound = true;
44610             Roo.TaskMgr.start({
44611                 run : this.bindHandler,
44612                 interval : this.monitorPoll || 200,
44613                 scope: this
44614             });
44615         }
44616     },
44617
44618     /**
44619      * Stops monitoring of the valid state of this form
44620      */
44621     stopMonitoring : function(){
44622         this.bound = false;
44623     },
44624
44625     // private
44626     bindHandler : function(){
44627         if(!this.bound){
44628             return false; // stops binding
44629         }
44630         var valid = true;
44631         this.items.each(function(f){
44632             if(!f.isValid(true)){
44633                 valid = false;
44634                 return false;
44635             }
44636         });
44637         for(var i = 0, len = this.buttons.length; i < len; i++){
44638             var btn = this.buttons[i];
44639             if(btn.formBind === true && btn.disabled === valid){
44640                 btn.setDisabled(!valid);
44641             }
44642         }
44643         this.fireEvent('clientvalidation', this, valid);
44644     }
44645     
44646     
44647     
44648     
44649     
44650     
44651     
44652     
44653 });
44654
44655
44656 // back compat
44657 Roo.Form = Roo.form.Form;
44658 /*
44659  * Based on:
44660  * Ext JS Library 1.1.1
44661  * Copyright(c) 2006-2007, Ext JS, LLC.
44662  *
44663  * Originally Released Under LGPL - original licence link has changed is not relivant.
44664  *
44665  * Fork - LGPL
44666  * <script type="text/javascript">
44667  */
44668
44669 // as we use this in bootstrap.
44670 Roo.namespace('Roo.form');
44671  /**
44672  * @class Roo.form.Action
44673  * Internal Class used to handle form actions
44674  * @constructor
44675  * @param {Roo.form.BasicForm} el The form element or its id
44676  * @param {Object} config Configuration options
44677  */
44678
44679  
44680  
44681 // define the action interface
44682 Roo.form.Action = function(form, options){
44683     this.form = form;
44684     this.options = options || {};
44685 };
44686 /**
44687  * Client Validation Failed
44688  * @const 
44689  */
44690 Roo.form.Action.CLIENT_INVALID = 'client';
44691 /**
44692  * Server Validation Failed
44693  * @const 
44694  */
44695 Roo.form.Action.SERVER_INVALID = 'server';
44696  /**
44697  * Connect to Server Failed
44698  * @const 
44699  */
44700 Roo.form.Action.CONNECT_FAILURE = 'connect';
44701 /**
44702  * Reading Data from Server Failed
44703  * @const 
44704  */
44705 Roo.form.Action.LOAD_FAILURE = 'load';
44706
44707 Roo.form.Action.prototype = {
44708     type : 'default',
44709     failureType : undefined,
44710     response : undefined,
44711     result : undefined,
44712
44713     // interface method
44714     run : function(options){
44715
44716     },
44717
44718     // interface method
44719     success : function(response){
44720
44721     },
44722
44723     // interface method
44724     handleResponse : function(response){
44725
44726     },
44727
44728     // default connection failure
44729     failure : function(response){
44730         
44731         this.response = response;
44732         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44733         this.form.afterAction(this, false);
44734     },
44735
44736     processResponse : function(response){
44737         this.response = response;
44738         if(!response.responseText){
44739             return true;
44740         }
44741         this.result = this.handleResponse(response);
44742         return this.result;
44743     },
44744
44745     // utility functions used internally
44746     getUrl : function(appendParams){
44747         var url = this.options.url || this.form.url || this.form.el.dom.action;
44748         if(appendParams){
44749             var p = this.getParams();
44750             if(p){
44751                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44752             }
44753         }
44754         return url;
44755     },
44756
44757     getMethod : function(){
44758         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44759     },
44760
44761     getParams : function(){
44762         var bp = this.form.baseParams;
44763         var p = this.options.params;
44764         if(p){
44765             if(typeof p == "object"){
44766                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44767             }else if(typeof p == 'string' && bp){
44768                 p += '&' + Roo.urlEncode(bp);
44769             }
44770         }else if(bp){
44771             p = Roo.urlEncode(bp);
44772         }
44773         return p;
44774     },
44775
44776     createCallback : function(){
44777         return {
44778             success: this.success,
44779             failure: this.failure,
44780             scope: this,
44781             timeout: (this.form.timeout*1000),
44782             upload: this.form.fileUpload ? this.success : undefined
44783         };
44784     }
44785 };
44786
44787 Roo.form.Action.Submit = function(form, options){
44788     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44789 };
44790
44791 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44792     type : 'submit',
44793
44794     haveProgress : false,
44795     uploadComplete : false,
44796     
44797     // uploadProgress indicator.
44798     uploadProgress : function()
44799     {
44800         if (!this.form.progressUrl) {
44801             return;
44802         }
44803         
44804         if (!this.haveProgress) {
44805             Roo.MessageBox.progress("Uploading", "Uploading");
44806         }
44807         if (this.uploadComplete) {
44808            Roo.MessageBox.hide();
44809            return;
44810         }
44811         
44812         this.haveProgress = true;
44813    
44814         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44815         
44816         var c = new Roo.data.Connection();
44817         c.request({
44818             url : this.form.progressUrl,
44819             params: {
44820                 id : uid
44821             },
44822             method: 'GET',
44823             success : function(req){
44824                //console.log(data);
44825                 var rdata = false;
44826                 var edata;
44827                 try  {
44828                    rdata = Roo.decode(req.responseText)
44829                 } catch (e) {
44830                     Roo.log("Invalid data from server..");
44831                     Roo.log(edata);
44832                     return;
44833                 }
44834                 if (!rdata || !rdata.success) {
44835                     Roo.log(rdata);
44836                     Roo.MessageBox.alert(Roo.encode(rdata));
44837                     return;
44838                 }
44839                 var data = rdata.data;
44840                 
44841                 if (this.uploadComplete) {
44842                    Roo.MessageBox.hide();
44843                    return;
44844                 }
44845                    
44846                 if (data){
44847                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44848                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44849                     );
44850                 }
44851                 this.uploadProgress.defer(2000,this);
44852             },
44853        
44854             failure: function(data) {
44855                 Roo.log('progress url failed ');
44856                 Roo.log(data);
44857             },
44858             scope : this
44859         });
44860            
44861     },
44862     
44863     
44864     run : function()
44865     {
44866         // run get Values on the form, so it syncs any secondary forms.
44867         this.form.getValues();
44868         
44869         var o = this.options;
44870         var method = this.getMethod();
44871         var isPost = method == 'POST';
44872         if(o.clientValidation === false || this.form.isValid()){
44873             
44874             if (this.form.progressUrl) {
44875                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44876                     (new Date() * 1) + '' + Math.random());
44877                     
44878             } 
44879             
44880             
44881             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44882                 form:this.form.el.dom,
44883                 url:this.getUrl(!isPost),
44884                 method: method,
44885                 params:isPost ? this.getParams() : null,
44886                 isUpload: this.form.fileUpload
44887             }));
44888             
44889             this.uploadProgress();
44890
44891         }else if (o.clientValidation !== false){ // client validation failed
44892             this.failureType = Roo.form.Action.CLIENT_INVALID;
44893             this.form.afterAction(this, false);
44894         }
44895     },
44896
44897     success : function(response)
44898     {
44899         this.uploadComplete= true;
44900         if (this.haveProgress) {
44901             Roo.MessageBox.hide();
44902         }
44903         
44904         
44905         var result = this.processResponse(response);
44906         if(result === true || result.success){
44907             this.form.afterAction(this, true);
44908             return;
44909         }
44910         if(result.errors){
44911             this.form.markInvalid(result.errors);
44912             this.failureType = Roo.form.Action.SERVER_INVALID;
44913         }
44914         this.form.afterAction(this, false);
44915     },
44916     failure : function(response)
44917     {
44918         this.uploadComplete= true;
44919         if (this.haveProgress) {
44920             Roo.MessageBox.hide();
44921         }
44922         
44923         this.response = response;
44924         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44925         this.form.afterAction(this, false);
44926     },
44927     
44928     handleResponse : function(response){
44929         if(this.form.errorReader){
44930             var rs = this.form.errorReader.read(response);
44931             var errors = [];
44932             if(rs.records){
44933                 for(var i = 0, len = rs.records.length; i < len; i++) {
44934                     var r = rs.records[i];
44935                     errors[i] = r.data;
44936                 }
44937             }
44938             if(errors.length < 1){
44939                 errors = null;
44940             }
44941             return {
44942                 success : rs.success,
44943                 errors : errors
44944             };
44945         }
44946         var ret = false;
44947         try {
44948             ret = Roo.decode(response.responseText);
44949         } catch (e) {
44950             ret = {
44951                 success: false,
44952                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44953                 errors : []
44954             };
44955         }
44956         return ret;
44957         
44958     }
44959 });
44960
44961
44962 Roo.form.Action.Load = function(form, options){
44963     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44964     this.reader = this.form.reader;
44965 };
44966
44967 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44968     type : 'load',
44969
44970     run : function(){
44971         
44972         Roo.Ajax.request(Roo.apply(
44973                 this.createCallback(), {
44974                     method:this.getMethod(),
44975                     url:this.getUrl(false),
44976                     params:this.getParams()
44977         }));
44978     },
44979
44980     success : function(response){
44981         
44982         var result = this.processResponse(response);
44983         if(result === true || !result.success || !result.data){
44984             this.failureType = Roo.form.Action.LOAD_FAILURE;
44985             this.form.afterAction(this, false);
44986             return;
44987         }
44988         this.form.clearInvalid();
44989         this.form.setValues(result.data);
44990         this.form.afterAction(this, true);
44991     },
44992
44993     handleResponse : function(response){
44994         if(this.form.reader){
44995             var rs = this.form.reader.read(response);
44996             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44997             return {
44998                 success : rs.success,
44999                 data : data
45000             };
45001         }
45002         return Roo.decode(response.responseText);
45003     }
45004 });
45005
45006 Roo.form.Action.ACTION_TYPES = {
45007     'load' : Roo.form.Action.Load,
45008     'submit' : Roo.form.Action.Submit
45009 };/*
45010  * Based on:
45011  * Ext JS Library 1.1.1
45012  * Copyright(c) 2006-2007, Ext JS, LLC.
45013  *
45014  * Originally Released Under LGPL - original licence link has changed is not relivant.
45015  *
45016  * Fork - LGPL
45017  * <script type="text/javascript">
45018  */
45019  
45020 /**
45021  * @class Roo.form.Layout
45022  * @extends Roo.Component
45023  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45024  * @constructor
45025  * @param {Object} config Configuration options
45026  */
45027 Roo.form.Layout = function(config){
45028     var xitems = [];
45029     if (config.items) {
45030         xitems = config.items;
45031         delete config.items;
45032     }
45033     Roo.form.Layout.superclass.constructor.call(this, config);
45034     this.stack = [];
45035     Roo.each(xitems, this.addxtype, this);
45036      
45037 };
45038
45039 Roo.extend(Roo.form.Layout, Roo.Component, {
45040     /**
45041      * @cfg {String/Object} autoCreate
45042      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45043      */
45044     /**
45045      * @cfg {String/Object/Function} style
45046      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45047      * a function which returns such a specification.
45048      */
45049     /**
45050      * @cfg {String} labelAlign
45051      * Valid values are "left," "top" and "right" (defaults to "left")
45052      */
45053     /**
45054      * @cfg {Number} labelWidth
45055      * Fixed width in pixels of all field labels (defaults to undefined)
45056      */
45057     /**
45058      * @cfg {Boolean} clear
45059      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45060      */
45061     clear : true,
45062     /**
45063      * @cfg {String} labelSeparator
45064      * The separator to use after field labels (defaults to ':')
45065      */
45066     labelSeparator : ':',
45067     /**
45068      * @cfg {Boolean} hideLabels
45069      * True to suppress the display of field labels in this layout (defaults to false)
45070      */
45071     hideLabels : false,
45072
45073     // private
45074     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45075     
45076     isLayout : true,
45077     
45078     // private
45079     onRender : function(ct, position){
45080         if(this.el){ // from markup
45081             this.el = Roo.get(this.el);
45082         }else {  // generate
45083             var cfg = this.getAutoCreate();
45084             this.el = ct.createChild(cfg, position);
45085         }
45086         if(this.style){
45087             this.el.applyStyles(this.style);
45088         }
45089         if(this.labelAlign){
45090             this.el.addClass('x-form-label-'+this.labelAlign);
45091         }
45092         if(this.hideLabels){
45093             this.labelStyle = "display:none";
45094             this.elementStyle = "padding-left:0;";
45095         }else{
45096             if(typeof this.labelWidth == 'number'){
45097                 this.labelStyle = "width:"+this.labelWidth+"px;";
45098                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45099             }
45100             if(this.labelAlign == 'top'){
45101                 this.labelStyle = "width:auto;";
45102                 this.elementStyle = "padding-left:0;";
45103             }
45104         }
45105         var stack = this.stack;
45106         var slen = stack.length;
45107         if(slen > 0){
45108             if(!this.fieldTpl){
45109                 var t = new Roo.Template(
45110                     '<div class="x-form-item {5}">',
45111                         '<label for="{0}" style="{2}">{1}{4}</label>',
45112                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45113                         '</div>',
45114                     '</div><div class="x-form-clear-left"></div>'
45115                 );
45116                 t.disableFormats = true;
45117                 t.compile();
45118                 Roo.form.Layout.prototype.fieldTpl = t;
45119             }
45120             for(var i = 0; i < slen; i++) {
45121                 if(stack[i].isFormField){
45122                     this.renderField(stack[i]);
45123                 }else{
45124                     this.renderComponent(stack[i]);
45125                 }
45126             }
45127         }
45128         if(this.clear){
45129             this.el.createChild({cls:'x-form-clear'});
45130         }
45131     },
45132
45133     // private
45134     renderField : function(f){
45135         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45136                f.id, //0
45137                f.fieldLabel, //1
45138                f.labelStyle||this.labelStyle||'', //2
45139                this.elementStyle||'', //3
45140                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45141                f.itemCls||this.itemCls||''  //5
45142        ], true).getPrevSibling());
45143     },
45144
45145     // private
45146     renderComponent : function(c){
45147         c.render(c.isLayout ? this.el : this.el.createChild());    
45148     },
45149     /**
45150      * Adds a object form elements (using the xtype property as the factory method.)
45151      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45152      * @param {Object} config 
45153      */
45154     addxtype : function(o)
45155     {
45156         // create the lement.
45157         o.form = this.form;
45158         var fe = Roo.factory(o, Roo.form);
45159         this.form.allItems.push(fe);
45160         this.stack.push(fe);
45161         
45162         if (fe.isFormField) {
45163             this.form.items.add(fe);
45164         }
45165          
45166         return fe;
45167     }
45168 });
45169
45170 /**
45171  * @class Roo.form.Column
45172  * @extends Roo.form.Layout
45173  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45174  * @constructor
45175  * @param {Object} config Configuration options
45176  */
45177 Roo.form.Column = function(config){
45178     Roo.form.Column.superclass.constructor.call(this, config);
45179 };
45180
45181 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45182     /**
45183      * @cfg {Number/String} width
45184      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45185      */
45186     /**
45187      * @cfg {String/Object} autoCreate
45188      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45189      */
45190
45191     // private
45192     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45193
45194     // private
45195     onRender : function(ct, position){
45196         Roo.form.Column.superclass.onRender.call(this, ct, position);
45197         if(this.width){
45198             this.el.setWidth(this.width);
45199         }
45200     }
45201 });
45202
45203
45204 /**
45205  * @class Roo.form.Row
45206  * @extends Roo.form.Layout
45207  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45208  * @constructor
45209  * @param {Object} config Configuration options
45210  */
45211
45212  
45213 Roo.form.Row = function(config){
45214     Roo.form.Row.superclass.constructor.call(this, config);
45215 };
45216  
45217 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45218       /**
45219      * @cfg {Number/String} width
45220      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45221      */
45222     /**
45223      * @cfg {Number/String} height
45224      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45225      */
45226     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45227     
45228     padWidth : 20,
45229     // private
45230     onRender : function(ct, position){
45231         //console.log('row render');
45232         if(!this.rowTpl){
45233             var t = new Roo.Template(
45234                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45235                     '<label for="{0}" style="{2}">{1}{4}</label>',
45236                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45237                     '</div>',
45238                 '</div>'
45239             );
45240             t.disableFormats = true;
45241             t.compile();
45242             Roo.form.Layout.prototype.rowTpl = t;
45243         }
45244         this.fieldTpl = this.rowTpl;
45245         
45246         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45247         var labelWidth = 100;
45248         
45249         if ((this.labelAlign != 'top')) {
45250             if (typeof this.labelWidth == 'number') {
45251                 labelWidth = this.labelWidth
45252             }
45253             this.padWidth =  20 + labelWidth;
45254             
45255         }
45256         
45257         Roo.form.Column.superclass.onRender.call(this, ct, position);
45258         if(this.width){
45259             this.el.setWidth(this.width);
45260         }
45261         if(this.height){
45262             this.el.setHeight(this.height);
45263         }
45264     },
45265     
45266     // private
45267     renderField : function(f){
45268         f.fieldEl = this.fieldTpl.append(this.el, [
45269                f.id, f.fieldLabel,
45270                f.labelStyle||this.labelStyle||'',
45271                this.elementStyle||'',
45272                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45273                f.itemCls||this.itemCls||'',
45274                f.width ? f.width + this.padWidth : 160 + this.padWidth
45275        ],true);
45276     }
45277 });
45278  
45279
45280 /**
45281  * @class Roo.form.FieldSet
45282  * @extends Roo.form.Layout
45283  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45284  * @constructor
45285  * @param {Object} config Configuration options
45286  */
45287 Roo.form.FieldSet = function(config){
45288     Roo.form.FieldSet.superclass.constructor.call(this, config);
45289 };
45290
45291 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45292     /**
45293      * @cfg {String} legend
45294      * The text to display as the legend for the FieldSet (defaults to '')
45295      */
45296     /**
45297      * @cfg {String/Object} autoCreate
45298      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45299      */
45300
45301     // private
45302     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45303
45304     // private
45305     onRender : function(ct, position){
45306         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45307         if(this.legend){
45308             this.setLegend(this.legend);
45309         }
45310     },
45311
45312     // private
45313     setLegend : function(text){
45314         if(this.rendered){
45315             this.el.child('legend').update(text);
45316         }
45317     }
45318 });/*
45319  * Based on:
45320  * Ext JS Library 1.1.1
45321  * Copyright(c) 2006-2007, Ext JS, LLC.
45322  *
45323  * Originally Released Under LGPL - original licence link has changed is not relivant.
45324  *
45325  * Fork - LGPL
45326  * <script type="text/javascript">
45327  */
45328 /**
45329  * @class Roo.form.VTypes
45330  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45331  * @singleton
45332  */
45333 Roo.form.VTypes = function(){
45334     // closure these in so they are only created once.
45335     var alpha = /^[a-zA-Z_]+$/;
45336     var alphanum = /^[a-zA-Z0-9_]+$/;
45337     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45338     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45339
45340     // All these messages and functions are configurable
45341     return {
45342         /**
45343          * The function used to validate email addresses
45344          * @param {String} value The email address
45345          */
45346         'email' : function(v){
45347             return email.test(v);
45348         },
45349         /**
45350          * The error text to display when the email validation function returns false
45351          * @type String
45352          */
45353         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45354         /**
45355          * The keystroke filter mask to be applied on email input
45356          * @type RegExp
45357          */
45358         'emailMask' : /[a-z0-9_\.\-@]/i,
45359
45360         /**
45361          * The function used to validate URLs
45362          * @param {String} value The URL
45363          */
45364         'url' : function(v){
45365             return url.test(v);
45366         },
45367         /**
45368          * The error text to display when the url validation function returns false
45369          * @type String
45370          */
45371         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45372         
45373         /**
45374          * The function used to validate alpha values
45375          * @param {String} value The value
45376          */
45377         'alpha' : function(v){
45378             return alpha.test(v);
45379         },
45380         /**
45381          * The error text to display when the alpha validation function returns false
45382          * @type String
45383          */
45384         'alphaText' : 'This field should only contain letters and _',
45385         /**
45386          * The keystroke filter mask to be applied on alpha input
45387          * @type RegExp
45388          */
45389         'alphaMask' : /[a-z_]/i,
45390
45391         /**
45392          * The function used to validate alphanumeric values
45393          * @param {String} value The value
45394          */
45395         'alphanum' : function(v){
45396             return alphanum.test(v);
45397         },
45398         /**
45399          * The error text to display when the alphanumeric validation function returns false
45400          * @type String
45401          */
45402         'alphanumText' : 'This field should only contain letters, numbers and _',
45403         /**
45404          * The keystroke filter mask to be applied on alphanumeric input
45405          * @type RegExp
45406          */
45407         'alphanumMask' : /[a-z0-9_]/i
45408     };
45409 }();//<script type="text/javascript">
45410
45411 /**
45412  * @class Roo.form.FCKeditor
45413  * @extends Roo.form.TextArea
45414  * Wrapper around the FCKEditor http://www.fckeditor.net
45415  * @constructor
45416  * Creates a new FCKeditor
45417  * @param {Object} config Configuration options
45418  */
45419 Roo.form.FCKeditor = function(config){
45420     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45421     this.addEvents({
45422          /**
45423          * @event editorinit
45424          * Fired when the editor is initialized - you can add extra handlers here..
45425          * @param {FCKeditor} this
45426          * @param {Object} the FCK object.
45427          */
45428         editorinit : true
45429     });
45430     
45431     
45432 };
45433 Roo.form.FCKeditor.editors = { };
45434 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45435 {
45436     //defaultAutoCreate : {
45437     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45438     //},
45439     // private
45440     /**
45441      * @cfg {Object} fck options - see fck manual for details.
45442      */
45443     fckconfig : false,
45444     
45445     /**
45446      * @cfg {Object} fck toolbar set (Basic or Default)
45447      */
45448     toolbarSet : 'Basic',
45449     /**
45450      * @cfg {Object} fck BasePath
45451      */ 
45452     basePath : '/fckeditor/',
45453     
45454     
45455     frame : false,
45456     
45457     value : '',
45458     
45459    
45460     onRender : function(ct, position)
45461     {
45462         if(!this.el){
45463             this.defaultAutoCreate = {
45464                 tag: "textarea",
45465                 style:"width:300px;height:60px;",
45466                 autocomplete: "off"
45467             };
45468         }
45469         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45470         /*
45471         if(this.grow){
45472             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45473             if(this.preventScrollbars){
45474                 this.el.setStyle("overflow", "hidden");
45475             }
45476             this.el.setHeight(this.growMin);
45477         }
45478         */
45479         //console.log('onrender' + this.getId() );
45480         Roo.form.FCKeditor.editors[this.getId()] = this;
45481          
45482
45483         this.replaceTextarea() ;
45484         
45485     },
45486     
45487     getEditor : function() {
45488         return this.fckEditor;
45489     },
45490     /**
45491      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45492      * @param {Mixed} value The value to set
45493      */
45494     
45495     
45496     setValue : function(value)
45497     {
45498         //console.log('setValue: ' + value);
45499         
45500         if(typeof(value) == 'undefined') { // not sure why this is happending...
45501             return;
45502         }
45503         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45504         
45505         //if(!this.el || !this.getEditor()) {
45506         //    this.value = value;
45507             //this.setValue.defer(100,this,[value]);    
45508         //    return;
45509         //} 
45510         
45511         if(!this.getEditor()) {
45512             return;
45513         }
45514         
45515         this.getEditor().SetData(value);
45516         
45517         //
45518
45519     },
45520
45521     /**
45522      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45523      * @return {Mixed} value The field value
45524      */
45525     getValue : function()
45526     {
45527         
45528         if (this.frame && this.frame.dom.style.display == 'none') {
45529             return Roo.form.FCKeditor.superclass.getValue.call(this);
45530         }
45531         
45532         if(!this.el || !this.getEditor()) {
45533            
45534            // this.getValue.defer(100,this); 
45535             return this.value;
45536         }
45537        
45538         
45539         var value=this.getEditor().GetData();
45540         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45541         return Roo.form.FCKeditor.superclass.getValue.call(this);
45542         
45543
45544     },
45545
45546     /**
45547      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45548      * @return {Mixed} value The field value
45549      */
45550     getRawValue : function()
45551     {
45552         if (this.frame && this.frame.dom.style.display == 'none') {
45553             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45554         }
45555         
45556         if(!this.el || !this.getEditor()) {
45557             //this.getRawValue.defer(100,this); 
45558             return this.value;
45559             return;
45560         }
45561         
45562         
45563         
45564         var value=this.getEditor().GetData();
45565         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45566         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45567          
45568     },
45569     
45570     setSize : function(w,h) {
45571         
45572         
45573         
45574         //if (this.frame && this.frame.dom.style.display == 'none') {
45575         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45576         //    return;
45577         //}
45578         //if(!this.el || !this.getEditor()) {
45579         //    this.setSize.defer(100,this, [w,h]); 
45580         //    return;
45581         //}
45582         
45583         
45584         
45585         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45586         
45587         this.frame.dom.setAttribute('width', w);
45588         this.frame.dom.setAttribute('height', h);
45589         this.frame.setSize(w,h);
45590         
45591     },
45592     
45593     toggleSourceEdit : function(value) {
45594         
45595       
45596          
45597         this.el.dom.style.display = value ? '' : 'none';
45598         this.frame.dom.style.display = value ?  'none' : '';
45599         
45600     },
45601     
45602     
45603     focus: function(tag)
45604     {
45605         if (this.frame.dom.style.display == 'none') {
45606             return Roo.form.FCKeditor.superclass.focus.call(this);
45607         }
45608         if(!this.el || !this.getEditor()) {
45609             this.focus.defer(100,this, [tag]); 
45610             return;
45611         }
45612         
45613         
45614         
45615         
45616         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45617         this.getEditor().Focus();
45618         if (tgs.length) {
45619             if (!this.getEditor().Selection.GetSelection()) {
45620                 this.focus.defer(100,this, [tag]); 
45621                 return;
45622             }
45623             
45624             
45625             var r = this.getEditor().EditorDocument.createRange();
45626             r.setStart(tgs[0],0);
45627             r.setEnd(tgs[0],0);
45628             this.getEditor().Selection.GetSelection().removeAllRanges();
45629             this.getEditor().Selection.GetSelection().addRange(r);
45630             this.getEditor().Focus();
45631         }
45632         
45633     },
45634     
45635     
45636     
45637     replaceTextarea : function()
45638     {
45639         if ( document.getElementById( this.getId() + '___Frame' ) )
45640             return ;
45641         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45642         //{
45643             // We must check the elements firstly using the Id and then the name.
45644         var oTextarea = document.getElementById( this.getId() );
45645         
45646         var colElementsByName = document.getElementsByName( this.getId() ) ;
45647          
45648         oTextarea.style.display = 'none' ;
45649
45650         if ( oTextarea.tabIndex ) {            
45651             this.TabIndex = oTextarea.tabIndex ;
45652         }
45653         
45654         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45655         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45656         this.frame = Roo.get(this.getId() + '___Frame')
45657     },
45658     
45659     _getConfigHtml : function()
45660     {
45661         var sConfig = '' ;
45662
45663         for ( var o in this.fckconfig ) {
45664             sConfig += sConfig.length > 0  ? '&amp;' : '';
45665             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45666         }
45667
45668         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45669     },
45670     
45671     
45672     _getIFrameHtml : function()
45673     {
45674         var sFile = 'fckeditor.html' ;
45675         /* no idea what this is about..
45676         try
45677         {
45678             if ( (/fcksource=true/i).test( window.top.location.search ) )
45679                 sFile = 'fckeditor.original.html' ;
45680         }
45681         catch (e) { 
45682         */
45683
45684         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45685         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45686         
45687         
45688         var html = '<iframe id="' + this.getId() +
45689             '___Frame" src="' + sLink +
45690             '" width="' + this.width +
45691             '" height="' + this.height + '"' +
45692             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45693             ' frameborder="0" scrolling="no"></iframe>' ;
45694
45695         return html ;
45696     },
45697     
45698     _insertHtmlBefore : function( html, element )
45699     {
45700         if ( element.insertAdjacentHTML )       {
45701             // IE
45702             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45703         } else { // Gecko
45704             var oRange = document.createRange() ;
45705             oRange.setStartBefore( element ) ;
45706             var oFragment = oRange.createContextualFragment( html );
45707             element.parentNode.insertBefore( oFragment, element ) ;
45708         }
45709     }
45710     
45711     
45712   
45713     
45714     
45715     
45716     
45717
45718 });
45719
45720 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45721
45722 function FCKeditor_OnComplete(editorInstance){
45723     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45724     f.fckEditor = editorInstance;
45725     //console.log("loaded");
45726     f.fireEvent('editorinit', f, editorInstance);
45727
45728   
45729
45730  
45731
45732
45733
45734
45735
45736
45737
45738
45739
45740
45741
45742
45743
45744
45745
45746 //<script type="text/javascript">
45747 /**
45748  * @class Roo.form.GridField
45749  * @extends Roo.form.Field
45750  * Embed a grid (or editable grid into a form)
45751  * STATUS ALPHA
45752  * 
45753  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45754  * it needs 
45755  * xgrid.store = Roo.data.Store
45756  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45757  * xgrid.store.reader = Roo.data.JsonReader 
45758  * 
45759  * 
45760  * @constructor
45761  * Creates a new GridField
45762  * @param {Object} config Configuration options
45763  */
45764 Roo.form.GridField = function(config){
45765     Roo.form.GridField.superclass.constructor.call(this, config);
45766      
45767 };
45768
45769 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45770     /**
45771      * @cfg {Number} width  - used to restrict width of grid..
45772      */
45773     width : 100,
45774     /**
45775      * @cfg {Number} height - used to restrict height of grid..
45776      */
45777     height : 50,
45778      /**
45779      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45780          * 
45781          *}
45782      */
45783     xgrid : false, 
45784     /**
45785      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45786      * {tag: "input", type: "checkbox", autocomplete: "off"})
45787      */
45788    // defaultAutoCreate : { tag: 'div' },
45789     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45790     /**
45791      * @cfg {String} addTitle Text to include for adding a title.
45792      */
45793     addTitle : false,
45794     //
45795     onResize : function(){
45796         Roo.form.Field.superclass.onResize.apply(this, arguments);
45797     },
45798
45799     initEvents : function(){
45800         // Roo.form.Checkbox.superclass.initEvents.call(this);
45801         // has no events...
45802        
45803     },
45804
45805
45806     getResizeEl : function(){
45807         return this.wrap;
45808     },
45809
45810     getPositionEl : function(){
45811         return this.wrap;
45812     },
45813
45814     // private
45815     onRender : function(ct, position){
45816         
45817         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45818         var style = this.style;
45819         delete this.style;
45820         
45821         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45822         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45823         this.viewEl = this.wrap.createChild({ tag: 'div' });
45824         if (style) {
45825             this.viewEl.applyStyles(style);
45826         }
45827         if (this.width) {
45828             this.viewEl.setWidth(this.width);
45829         }
45830         if (this.height) {
45831             this.viewEl.setHeight(this.height);
45832         }
45833         //if(this.inputValue !== undefined){
45834         //this.setValue(this.value);
45835         
45836         
45837         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45838         
45839         
45840         this.grid.render();
45841         this.grid.getDataSource().on('remove', this.refreshValue, this);
45842         this.grid.getDataSource().on('update', this.refreshValue, this);
45843         this.grid.on('afteredit', this.refreshValue, this);
45844  
45845     },
45846      
45847     
45848     /**
45849      * Sets the value of the item. 
45850      * @param {String} either an object  or a string..
45851      */
45852     setValue : function(v){
45853         //this.value = v;
45854         v = v || []; // empty set..
45855         // this does not seem smart - it really only affects memoryproxy grids..
45856         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45857             var ds = this.grid.getDataSource();
45858             // assumes a json reader..
45859             var data = {}
45860             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45861             ds.loadData( data);
45862         }
45863         // clear selection so it does not get stale.
45864         if (this.grid.sm) { 
45865             this.grid.sm.clearSelections();
45866         }
45867         
45868         Roo.form.GridField.superclass.setValue.call(this, v);
45869         this.refreshValue();
45870         // should load data in the grid really....
45871     },
45872     
45873     // private
45874     refreshValue: function() {
45875          var val = [];
45876         this.grid.getDataSource().each(function(r) {
45877             val.push(r.data);
45878         });
45879         this.el.dom.value = Roo.encode(val);
45880     }
45881     
45882      
45883     
45884     
45885 });/*
45886  * Based on:
45887  * Ext JS Library 1.1.1
45888  * Copyright(c) 2006-2007, Ext JS, LLC.
45889  *
45890  * Originally Released Under LGPL - original licence link has changed is not relivant.
45891  *
45892  * Fork - LGPL
45893  * <script type="text/javascript">
45894  */
45895 /**
45896  * @class Roo.form.DisplayField
45897  * @extends Roo.form.Field
45898  * A generic Field to display non-editable data.
45899  * @constructor
45900  * Creates a new Display Field item.
45901  * @param {Object} config Configuration options
45902  */
45903 Roo.form.DisplayField = function(config){
45904     Roo.form.DisplayField.superclass.constructor.call(this, config);
45905     
45906 };
45907
45908 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45909     inputType:      'hidden',
45910     allowBlank:     true,
45911     readOnly:         true,
45912     
45913  
45914     /**
45915      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45916      */
45917     focusClass : undefined,
45918     /**
45919      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45920      */
45921     fieldClass: 'x-form-field',
45922     
45923      /**
45924      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45925      */
45926     valueRenderer: undefined,
45927     
45928     width: 100,
45929     /**
45930      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45931      * {tag: "input", type: "checkbox", autocomplete: "off"})
45932      */
45933      
45934  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45935
45936     onResize : function(){
45937         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45938         
45939     },
45940
45941     initEvents : function(){
45942         // Roo.form.Checkbox.superclass.initEvents.call(this);
45943         // has no events...
45944        
45945     },
45946
45947
45948     getResizeEl : function(){
45949         return this.wrap;
45950     },
45951
45952     getPositionEl : function(){
45953         return this.wrap;
45954     },
45955
45956     // private
45957     onRender : function(ct, position){
45958         
45959         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45960         //if(this.inputValue !== undefined){
45961         this.wrap = this.el.wrap();
45962         
45963         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45964         
45965         if (this.bodyStyle) {
45966             this.viewEl.applyStyles(this.bodyStyle);
45967         }
45968         //this.viewEl.setStyle('padding', '2px');
45969         
45970         this.setValue(this.value);
45971         
45972     },
45973 /*
45974     // private
45975     initValue : Roo.emptyFn,
45976
45977   */
45978
45979         // private
45980     onClick : function(){
45981         
45982     },
45983
45984     /**
45985      * Sets the checked state of the checkbox.
45986      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45987      */
45988     setValue : function(v){
45989         this.value = v;
45990         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45991         // this might be called before we have a dom element..
45992         if (!this.viewEl) {
45993             return;
45994         }
45995         this.viewEl.dom.innerHTML = html;
45996         Roo.form.DisplayField.superclass.setValue.call(this, v);
45997
45998     }
45999 });/*
46000  * 
46001  * Licence- LGPL
46002  * 
46003  */
46004
46005 /**
46006  * @class Roo.form.DayPicker
46007  * @extends Roo.form.Field
46008  * A Day picker show [M] [T] [W] ....
46009  * @constructor
46010  * Creates a new Day Picker
46011  * @param {Object} config Configuration options
46012  */
46013 Roo.form.DayPicker= function(config){
46014     Roo.form.DayPicker.superclass.constructor.call(this, config);
46015      
46016 };
46017
46018 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46019     /**
46020      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46021      */
46022     focusClass : undefined,
46023     /**
46024      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46025      */
46026     fieldClass: "x-form-field",
46027    
46028     /**
46029      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46030      * {tag: "input", type: "checkbox", autocomplete: "off"})
46031      */
46032     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46033     
46034    
46035     actionMode : 'viewEl', 
46036     //
46037     // private
46038  
46039     inputType : 'hidden',
46040     
46041      
46042     inputElement: false, // real input element?
46043     basedOn: false, // ????
46044     
46045     isFormField: true, // not sure where this is needed!!!!
46046
46047     onResize : function(){
46048         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46049         if(!this.boxLabel){
46050             this.el.alignTo(this.wrap, 'c-c');
46051         }
46052     },
46053
46054     initEvents : function(){
46055         Roo.form.Checkbox.superclass.initEvents.call(this);
46056         this.el.on("click", this.onClick,  this);
46057         this.el.on("change", this.onClick,  this);
46058     },
46059
46060
46061     getResizeEl : function(){
46062         return this.wrap;
46063     },
46064
46065     getPositionEl : function(){
46066         return this.wrap;
46067     },
46068
46069     
46070     // private
46071     onRender : function(ct, position){
46072         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46073        
46074         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46075         
46076         var r1 = '<table><tr>';
46077         var r2 = '<tr class="x-form-daypick-icons">';
46078         for (var i=0; i < 7; i++) {
46079             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46080             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46081         }
46082         
46083         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46084         viewEl.select('img').on('click', this.onClick, this);
46085         this.viewEl = viewEl;   
46086         
46087         
46088         // this will not work on Chrome!!!
46089         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46090         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46091         
46092         
46093           
46094
46095     },
46096
46097     // private
46098     initValue : Roo.emptyFn,
46099
46100     /**
46101      * Returns the checked state of the checkbox.
46102      * @return {Boolean} True if checked, else false
46103      */
46104     getValue : function(){
46105         return this.el.dom.value;
46106         
46107     },
46108
46109         // private
46110     onClick : function(e){ 
46111         //this.setChecked(!this.checked);
46112         Roo.get(e.target).toggleClass('x-menu-item-checked');
46113         this.refreshValue();
46114         //if(this.el.dom.checked != this.checked){
46115         //    this.setValue(this.el.dom.checked);
46116        // }
46117     },
46118     
46119     // private
46120     refreshValue : function()
46121     {
46122         var val = '';
46123         this.viewEl.select('img',true).each(function(e,i,n)  {
46124             val += e.is(".x-menu-item-checked") ? String(n) : '';
46125         });
46126         this.setValue(val, true);
46127     },
46128
46129     /**
46130      * Sets the checked state of the checkbox.
46131      * On is always based on a string comparison between inputValue and the param.
46132      * @param {Boolean/String} value - the value to set 
46133      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46134      */
46135     setValue : function(v,suppressEvent){
46136         if (!this.el.dom) {
46137             return;
46138         }
46139         var old = this.el.dom.value ;
46140         this.el.dom.value = v;
46141         if (suppressEvent) {
46142             return ;
46143         }
46144          
46145         // update display..
46146         this.viewEl.select('img',true).each(function(e,i,n)  {
46147             
46148             var on = e.is(".x-menu-item-checked");
46149             var newv = v.indexOf(String(n)) > -1;
46150             if (on != newv) {
46151                 e.toggleClass('x-menu-item-checked');
46152             }
46153             
46154         });
46155         
46156         
46157         this.fireEvent('change', this, v, old);
46158         
46159         
46160     },
46161    
46162     // handle setting of hidden value by some other method!!?!?
46163     setFromHidden: function()
46164     {
46165         if(!this.el){
46166             return;
46167         }
46168         //console.log("SET FROM HIDDEN");
46169         //alert('setFrom hidden');
46170         this.setValue(this.el.dom.value);
46171     },
46172     
46173     onDestroy : function()
46174     {
46175         if(this.viewEl){
46176             Roo.get(this.viewEl).remove();
46177         }
46178          
46179         Roo.form.DayPicker.superclass.onDestroy.call(this);
46180     }
46181
46182 });/*
46183  * RooJS Library 1.1.1
46184  * Copyright(c) 2008-2011  Alan Knowles
46185  *
46186  * License - LGPL
46187  */
46188  
46189
46190 /**
46191  * @class Roo.form.ComboCheck
46192  * @extends Roo.form.ComboBox
46193  * A combobox for multiple select items.
46194  *
46195  * FIXME - could do with a reset button..
46196  * 
46197  * @constructor
46198  * Create a new ComboCheck
46199  * @param {Object} config Configuration options
46200  */
46201 Roo.form.ComboCheck = function(config){
46202     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46203     // should verify some data...
46204     // like
46205     // hiddenName = required..
46206     // displayField = required
46207     // valudField == required
46208     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46209     var _t = this;
46210     Roo.each(req, function(e) {
46211         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46212             throw "Roo.form.ComboCheck : missing value for: " + e;
46213         }
46214     });
46215     
46216     
46217 };
46218
46219 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46220      
46221      
46222     editable : false,
46223      
46224     selectedClass: 'x-menu-item-checked', 
46225     
46226     // private
46227     onRender : function(ct, position){
46228         var _t = this;
46229         
46230         
46231         
46232         if(!this.tpl){
46233             var cls = 'x-combo-list';
46234
46235             
46236             this.tpl =  new Roo.Template({
46237                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46238                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46239                    '<span>{' + this.displayField + '}</span>' +
46240                     '</div>' 
46241                 
46242             });
46243         }
46244  
46245         
46246         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46247         this.view.singleSelect = false;
46248         this.view.multiSelect = true;
46249         this.view.toggleSelect = true;
46250         this.pageTb.add(new Roo.Toolbar.Fill(), {
46251             
46252             text: 'Done',
46253             handler: function()
46254             {
46255                 _t.collapse();
46256             }
46257         });
46258     },
46259     
46260     onViewOver : function(e, t){
46261         // do nothing...
46262         return;
46263         
46264     },
46265     
46266     onViewClick : function(doFocus,index){
46267         return;
46268         
46269     },
46270     select: function () {
46271         //Roo.log("SELECT CALLED");
46272     },
46273      
46274     selectByValue : function(xv, scrollIntoView){
46275         var ar = this.getValueArray();
46276         var sels = [];
46277         
46278         Roo.each(ar, function(v) {
46279             if(v === undefined || v === null){
46280                 return;
46281             }
46282             var r = this.findRecord(this.valueField, v);
46283             if(r){
46284                 sels.push(this.store.indexOf(r))
46285                 
46286             }
46287         },this);
46288         this.view.select(sels);
46289         return false;
46290     },
46291     
46292     
46293     
46294     onSelect : function(record, index){
46295        // Roo.log("onselect Called");
46296        // this is only called by the clear button now..
46297         this.view.clearSelections();
46298         this.setValue('[]');
46299         if (this.value != this.valueBefore) {
46300             this.fireEvent('change', this, this.value, this.valueBefore);
46301             this.valueBefore = this.value;
46302         }
46303     },
46304     getValueArray : function()
46305     {
46306         var ar = [] ;
46307         
46308         try {
46309             //Roo.log(this.value);
46310             if (typeof(this.value) == 'undefined') {
46311                 return [];
46312             }
46313             var ar = Roo.decode(this.value);
46314             return  ar instanceof Array ? ar : []; //?? valid?
46315             
46316         } catch(e) {
46317             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46318             return [];
46319         }
46320          
46321     },
46322     expand : function ()
46323     {
46324         
46325         Roo.form.ComboCheck.superclass.expand.call(this);
46326         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46327         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46328         
46329
46330     },
46331     
46332     collapse : function(){
46333         Roo.form.ComboCheck.superclass.collapse.call(this);
46334         var sl = this.view.getSelectedIndexes();
46335         var st = this.store;
46336         var nv = [];
46337         var tv = [];
46338         var r;
46339         Roo.each(sl, function(i) {
46340             r = st.getAt(i);
46341             nv.push(r.get(this.valueField));
46342         },this);
46343         this.setValue(Roo.encode(nv));
46344         if (this.value != this.valueBefore) {
46345
46346             this.fireEvent('change', this, this.value, this.valueBefore);
46347             this.valueBefore = this.value;
46348         }
46349         
46350     },
46351     
46352     setValue : function(v){
46353         // Roo.log(v);
46354         this.value = v;
46355         
46356         var vals = this.getValueArray();
46357         var tv = [];
46358         Roo.each(vals, function(k) {
46359             var r = this.findRecord(this.valueField, k);
46360             if(r){
46361                 tv.push(r.data[this.displayField]);
46362             }else if(this.valueNotFoundText !== undefined){
46363                 tv.push( this.valueNotFoundText );
46364             }
46365         },this);
46366        // Roo.log(tv);
46367         
46368         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46369         this.hiddenField.value = v;
46370         this.value = v;
46371     }
46372     
46373 });/*
46374  * Based on:
46375  * Ext JS Library 1.1.1
46376  * Copyright(c) 2006-2007, Ext JS, LLC.
46377  *
46378  * Originally Released Under LGPL - original licence link has changed is not relivant.
46379  *
46380  * Fork - LGPL
46381  * <script type="text/javascript">
46382  */
46383  
46384 /**
46385  * @class Roo.form.Signature
46386  * @extends Roo.form.Field
46387  * Signature field.  
46388  * @constructor
46389  * 
46390  * @param {Object} config Configuration options
46391  */
46392
46393 Roo.form.Signature = function(config){
46394     Roo.form.Signature.superclass.constructor.call(this, config);
46395     
46396     this.addEvents({// not in used??
46397          /**
46398          * @event confirm
46399          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46400              * @param {Roo.form.Signature} combo This combo box
46401              */
46402         'confirm' : true,
46403         /**
46404          * @event reset
46405          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46406              * @param {Roo.form.ComboBox} combo This combo box
46407              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46408              */
46409         'reset' : true
46410     });
46411 };
46412
46413 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46414     /**
46415      * @cfg {Object} labels Label to use when rendering a form.
46416      * defaults to 
46417      * labels : { 
46418      *      clear : "Clear",
46419      *      confirm : "Confirm"
46420      *  }
46421      */
46422     labels : { 
46423         clear : "Clear",
46424         confirm : "Confirm"
46425     },
46426     /**
46427      * @cfg {Number} width The signature panel width (defaults to 300)
46428      */
46429     width: 300,
46430     /**
46431      * @cfg {Number} height The signature panel height (defaults to 100)
46432      */
46433     height : 100,
46434     /**
46435      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46436      */
46437     allowBlank : false,
46438     
46439     //private
46440     // {Object} signPanel The signature SVG panel element (defaults to {})
46441     signPanel : {},
46442     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46443     isMouseDown : false,
46444     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46445     isConfirmed : false,
46446     // {String} signatureTmp SVG mapping string (defaults to empty string)
46447     signatureTmp : '',
46448     
46449     
46450     defaultAutoCreate : { // modified by initCompnoent..
46451         tag: "input",
46452         type:"hidden"
46453     },
46454
46455     // private
46456     onRender : function(ct, position){
46457         
46458         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46459         
46460         this.wrap = this.el.wrap({
46461             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46462         });
46463         
46464         this.createToolbar(this);
46465         this.signPanel = this.wrap.createChild({
46466                 tag: 'div',
46467                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46468             }, this.el
46469         );
46470             
46471         this.svgID = Roo.id();
46472         this.svgEl = this.signPanel.createChild({
46473               xmlns : 'http://www.w3.org/2000/svg',
46474               tag : 'svg',
46475               id : this.svgID + "-svg",
46476               width: this.width,
46477               height: this.height,
46478               viewBox: '0 0 '+this.width+' '+this.height,
46479               cn : [
46480                 {
46481                     tag: "rect",
46482                     id: this.svgID + "-svg-r",
46483                     width: this.width,
46484                     height: this.height,
46485                     fill: "#ffa"
46486                 },
46487                 {
46488                     tag: "line",
46489                     id: this.svgID + "-svg-l",
46490                     x1: "0", // start
46491                     y1: (this.height*0.8), // start set the line in 80% of height
46492                     x2: this.width, // end
46493                     y2: (this.height*0.8), // end set the line in 80% of height
46494                     'stroke': "#666",
46495                     'stroke-width': "1",
46496                     'stroke-dasharray': "3",
46497                     'shape-rendering': "crispEdges",
46498                     'pointer-events': "none"
46499                 },
46500                 {
46501                     tag: "path",
46502                     id: this.svgID + "-svg-p",
46503                     'stroke': "navy",
46504                     'stroke-width': "3",
46505                     'fill': "none",
46506                     'pointer-events': 'none'
46507                 }
46508               ]
46509         });
46510         this.createSVG();
46511         this.svgBox = this.svgEl.dom.getScreenCTM();
46512     },
46513     createSVG : function(){ 
46514         var svg = this.signPanel;
46515         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46516         var t = this;
46517
46518         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46519         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46520         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46521         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46522         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46523         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46524         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46525         
46526     },
46527     isTouchEvent : function(e){
46528         return e.type.match(/^touch/);
46529     },
46530     getCoords : function (e) {
46531         var pt    = this.svgEl.dom.createSVGPoint();
46532         pt.x = e.clientX; 
46533         pt.y = e.clientY;
46534         if (this.isTouchEvent(e)) {
46535             pt.x =  e.targetTouches[0].clientX 
46536             pt.y = e.targetTouches[0].clientY;
46537         }
46538         var a = this.svgEl.dom.getScreenCTM();
46539         var b = a.inverse();
46540         var mx = pt.matrixTransform(b);
46541         return mx.x + ',' + mx.y;
46542     },
46543     //mouse event headler 
46544     down : function (e) {
46545         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46546         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46547         
46548         this.isMouseDown = true;
46549         
46550         e.preventDefault();
46551     },
46552     move : function (e) {
46553         if (this.isMouseDown) {
46554             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46555             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46556         }
46557         
46558         e.preventDefault();
46559     },
46560     up : function (e) {
46561         this.isMouseDown = false;
46562         var sp = this.signatureTmp.split(' ');
46563         
46564         if(sp.length > 1){
46565             if(!sp[sp.length-2].match(/^L/)){
46566                 sp.pop();
46567                 sp.pop();
46568                 sp.push("");
46569                 this.signatureTmp = sp.join(" ");
46570             }
46571         }
46572         if(this.getValue() != this.signatureTmp){
46573             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46574             this.isConfirmed = false;
46575         }
46576         e.preventDefault();
46577     },
46578     
46579     /**
46580      * Protected method that will not generally be called directly. It
46581      * is called when the editor creates its toolbar. Override this method if you need to
46582      * add custom toolbar buttons.
46583      * @param {HtmlEditor} editor
46584      */
46585     createToolbar : function(editor){
46586          function btn(id, toggle, handler){
46587             var xid = fid + '-'+ id ;
46588             return {
46589                 id : xid,
46590                 cmd : id,
46591                 cls : 'x-btn-icon x-edit-'+id,
46592                 enableToggle:toggle !== false,
46593                 scope: editor, // was editor...
46594                 handler:handler||editor.relayBtnCmd,
46595                 clickEvent:'mousedown',
46596                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46597                 tabIndex:-1
46598             };
46599         }
46600         
46601         
46602         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46603         this.tb = tb;
46604         this.tb.add(
46605            {
46606                 cls : ' x-signature-btn x-signature-'+id,
46607                 scope: editor, // was editor...
46608                 handler: this.reset,
46609                 clickEvent:'mousedown',
46610                 text: this.labels.clear
46611             },
46612             {
46613                  xtype : 'Fill',
46614                  xns: Roo.Toolbar
46615             }, 
46616             {
46617                 cls : '  x-signature-btn x-signature-'+id,
46618                 scope: editor, // was editor...
46619                 handler: this.confirmHandler,
46620                 clickEvent:'mousedown',
46621                 text: this.labels.confirm
46622             }
46623         );
46624     
46625     },
46626     //public
46627     /**
46628      * when user is clicked confirm then show this image.....
46629      * 
46630      * @return {String} Image Data URI
46631      */
46632     getImageDataURI : function(){
46633         var svg = this.svgEl.dom.parentNode.innerHTML;
46634         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46635         return src; 
46636     },
46637     /**
46638      * 
46639      * @return {Boolean} this.isConfirmed
46640      */
46641     getConfirmed : function(){
46642         return this.isConfirmed;
46643     },
46644     /**
46645      * 
46646      * @return {Number} this.width
46647      */
46648     getWidth : function(){
46649         return this.width;
46650     },
46651     /**
46652      * 
46653      * @return {Number} this.height
46654      */
46655     getHeight : function(){
46656         return this.height;
46657     },
46658     // private
46659     getSignature : function(){
46660         return this.signatureTmp;
46661     },
46662     // private
46663     reset : function(){
46664         this.signatureTmp = '';
46665         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46666         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46667         this.isConfirmed = false;
46668         Roo.form.Signature.superclass.reset.call(this);
46669     },
46670     setSignature : function(s){
46671         this.signatureTmp = s;
46672         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46673         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46674         this.setValue(s);
46675         this.isConfirmed = false;
46676         Roo.form.Signature.superclass.reset.call(this);
46677     }, 
46678     test : function(){
46679 //        Roo.log(this.signPanel.dom.contentWindow.up())
46680     },
46681     //private
46682     setConfirmed : function(){
46683         
46684         
46685         
46686 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46687     },
46688     // private
46689     confirmHandler : function(){
46690         if(!this.getSignature()){
46691             return;
46692         }
46693         
46694         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46695         this.setValue(this.getSignature());
46696         this.isConfirmed = true;
46697         
46698         this.fireEvent('confirm', this);
46699     },
46700     // private
46701     // Subclasses should provide the validation implementation by overriding this
46702     validateValue : function(value){
46703         if(this.allowBlank){
46704             return true;
46705         }
46706         
46707         if(this.isConfirmed){
46708             return true;
46709         }
46710         return false;
46711     }
46712 });/*
46713  * Based on:
46714  * Ext JS Library 1.1.1
46715  * Copyright(c) 2006-2007, Ext JS, LLC.
46716  *
46717  * Originally Released Under LGPL - original licence link has changed is not relivant.
46718  *
46719  * Fork - LGPL
46720  * <script type="text/javascript">
46721  */
46722  
46723
46724 /**
46725  * @class Roo.form.ComboBox
46726  * @extends Roo.form.TriggerField
46727  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46728  * @constructor
46729  * Create a new ComboBox.
46730  * @param {Object} config Configuration options
46731  */
46732 Roo.form.Select = function(config){
46733     Roo.form.Select.superclass.constructor.call(this, config);
46734      
46735 };
46736
46737 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46738     /**
46739      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46740      */
46741     /**
46742      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46743      * rendering into an Roo.Editor, defaults to false)
46744      */
46745     /**
46746      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46747      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46748      */
46749     /**
46750      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46751      */
46752     /**
46753      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46754      * the dropdown list (defaults to undefined, with no header element)
46755      */
46756
46757      /**
46758      * @cfg {String/Roo.Template} tpl The template to use to render the output
46759      */
46760      
46761     // private
46762     defaultAutoCreate : {tag: "select"  },
46763     /**
46764      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46765      */
46766     listWidth: undefined,
46767     /**
46768      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46769      * mode = 'remote' or 'text' if mode = 'local')
46770      */
46771     displayField: undefined,
46772     /**
46773      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46774      * mode = 'remote' or 'value' if mode = 'local'). 
46775      * Note: use of a valueField requires the user make a selection
46776      * in order for a value to be mapped.
46777      */
46778     valueField: undefined,
46779     
46780     
46781     /**
46782      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46783      * field's data value (defaults to the underlying DOM element's name)
46784      */
46785     hiddenName: undefined,
46786     /**
46787      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46788      */
46789     listClass: '',
46790     /**
46791      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46792      */
46793     selectedClass: 'x-combo-selected',
46794     /**
46795      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46796      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46797      * which displays a downward arrow icon).
46798      */
46799     triggerClass : 'x-form-arrow-trigger',
46800     /**
46801      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46802      */
46803     shadow:'sides',
46804     /**
46805      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46806      * anchor positions (defaults to 'tl-bl')
46807      */
46808     listAlign: 'tl-bl?',
46809     /**
46810      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46811      */
46812     maxHeight: 300,
46813     /**
46814      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46815      * query specified by the allQuery config option (defaults to 'query')
46816      */
46817     triggerAction: 'query',
46818     /**
46819      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46820      * (defaults to 4, does not apply if editable = false)
46821      */
46822     minChars : 4,
46823     /**
46824      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46825      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46826      */
46827     typeAhead: false,
46828     /**
46829      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46830      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46831      */
46832     queryDelay: 500,
46833     /**
46834      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46835      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46836      */
46837     pageSize: 0,
46838     /**
46839      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46840      * when editable = true (defaults to false)
46841      */
46842     selectOnFocus:false,
46843     /**
46844      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46845      */
46846     queryParam: 'query',
46847     /**
46848      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46849      * when mode = 'remote' (defaults to 'Loading...')
46850      */
46851     loadingText: 'Loading...',
46852     /**
46853      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46854      */
46855     resizable: false,
46856     /**
46857      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46858      */
46859     handleHeight : 8,
46860     /**
46861      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46862      * traditional select (defaults to true)
46863      */
46864     editable: true,
46865     /**
46866      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46867      */
46868     allQuery: '',
46869     /**
46870      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46871      */
46872     mode: 'remote',
46873     /**
46874      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46875      * listWidth has a higher value)
46876      */
46877     minListWidth : 70,
46878     /**
46879      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46880      * allow the user to set arbitrary text into the field (defaults to false)
46881      */
46882     forceSelection:false,
46883     /**
46884      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46885      * if typeAhead = true (defaults to 250)
46886      */
46887     typeAheadDelay : 250,
46888     /**
46889      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46890      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46891      */
46892     valueNotFoundText : undefined,
46893     
46894     /**
46895      * @cfg {String} defaultValue The value displayed after loading the store.
46896      */
46897     defaultValue: '',
46898     
46899     /**
46900      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46901      */
46902     blockFocus : false,
46903     
46904     /**
46905      * @cfg {Boolean} disableClear Disable showing of clear button.
46906      */
46907     disableClear : false,
46908     /**
46909      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46910      */
46911     alwaysQuery : false,
46912     
46913     //private
46914     addicon : false,
46915     editicon: false,
46916     
46917     // element that contains real text value.. (when hidden is used..)
46918      
46919     // private
46920     onRender : function(ct, position){
46921         Roo.form.Field.prototype.onRender.call(this, ct, position);
46922         
46923         if(this.store){
46924             this.store.on('beforeload', this.onBeforeLoad, this);
46925             this.store.on('load', this.onLoad, this);
46926             this.store.on('loadexception', this.onLoadException, this);
46927             this.store.load({});
46928         }
46929         
46930         
46931         
46932     },
46933
46934     // private
46935     initEvents : function(){
46936         //Roo.form.ComboBox.superclass.initEvents.call(this);
46937  
46938     },
46939
46940     onDestroy : function(){
46941        
46942         if(this.store){
46943             this.store.un('beforeload', this.onBeforeLoad, this);
46944             this.store.un('load', this.onLoad, this);
46945             this.store.un('loadexception', this.onLoadException, this);
46946         }
46947         //Roo.form.ComboBox.superclass.onDestroy.call(this);
46948     },
46949
46950     // private
46951     fireKey : function(e){
46952         if(e.isNavKeyPress() && !this.list.isVisible()){
46953             this.fireEvent("specialkey", this, e);
46954         }
46955     },
46956
46957     // private
46958     onResize: function(w, h){
46959         
46960         return; 
46961     
46962         
46963     },
46964
46965     /**
46966      * Allow or prevent the user from directly editing the field text.  If false is passed,
46967      * the user will only be able to select from the items defined in the dropdown list.  This method
46968      * is the runtime equivalent of setting the 'editable' config option at config time.
46969      * @param {Boolean} value True to allow the user to directly edit the field text
46970      */
46971     setEditable : function(value){
46972          
46973     },
46974
46975     // private
46976     onBeforeLoad : function(){
46977         
46978         Roo.log("Select before load");
46979         return;
46980     
46981         this.innerList.update(this.loadingText ?
46982                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46983         //this.restrictHeight();
46984         this.selectedIndex = -1;
46985     },
46986
46987     // private
46988     onLoad : function(){
46989
46990     
46991         var dom = this.el.dom;
46992         dom.innerHTML = '';
46993          var od = dom.ownerDocument;
46994          
46995         if (this.emptyText) {
46996             var op = od.createElement('option');
46997             op.setAttribute('value', '');
46998             op.innerHTML = String.format('{0}', this.emptyText);
46999             dom.appendChild(op);
47000         }
47001         if(this.store.getCount() > 0){
47002            
47003             var vf = this.valueField;
47004             var df = this.displayField;
47005             this.store.data.each(function(r) {
47006                 // which colmsn to use... testing - cdoe / title..
47007                 var op = od.createElement('option');
47008                 op.setAttribute('value', r.data[vf]);
47009                 op.innerHTML = String.format('{0}', r.data[df]);
47010                 dom.appendChild(op);
47011             });
47012             if (typeof(this.defaultValue != 'undefined')) {
47013                 this.setValue(this.defaultValue);
47014             }
47015             
47016              
47017         }else{
47018             //this.onEmptyResults();
47019         }
47020         //this.el.focus();
47021     },
47022     // private
47023     onLoadException : function()
47024     {
47025         dom.innerHTML = '';
47026             
47027         Roo.log("Select on load exception");
47028         return;
47029     
47030         this.collapse();
47031         Roo.log(this.store.reader.jsonData);
47032         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47033             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47034         }
47035         
47036         
47037     },
47038     // private
47039     onTypeAhead : function(){
47040          
47041     },
47042
47043     // private
47044     onSelect : function(record, index){
47045         Roo.log('on select?');
47046         return;
47047         if(this.fireEvent('beforeselect', this, record, index) !== false){
47048             this.setFromData(index > -1 ? record.data : false);
47049             this.collapse();
47050             this.fireEvent('select', this, record, index);
47051         }
47052     },
47053
47054     /**
47055      * Returns the currently selected field value or empty string if no value is set.
47056      * @return {String} value The selected value
47057      */
47058     getValue : function(){
47059         var dom = this.el.dom;
47060         this.value = dom.options[dom.selectedIndex].value;
47061         return this.value;
47062         
47063     },
47064
47065     /**
47066      * Clears any text/value currently set in the field
47067      */
47068     clearValue : function(){
47069         this.value = '';
47070         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47071         
47072     },
47073
47074     /**
47075      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47076      * will be displayed in the field.  If the value does not match the data value of an existing item,
47077      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47078      * Otherwise the field will be blank (although the value will still be set).
47079      * @param {String} value The value to match
47080      */
47081     setValue : function(v){
47082         var d = this.el.dom;
47083         for (var i =0; i < d.options.length;i++) {
47084             if (v == d.options[i].value) {
47085                 d.selectedIndex = i;
47086                 this.value = v;
47087                 return;
47088             }
47089         }
47090         this.clearValue();
47091     },
47092     /**
47093      * @property {Object} the last set data for the element
47094      */
47095     
47096     lastData : false,
47097     /**
47098      * Sets the value of the field based on a object which is related to the record format for the store.
47099      * @param {Object} value the value to set as. or false on reset?
47100      */
47101     setFromData : function(o){
47102         Roo.log('setfrom data?');
47103          
47104         
47105         
47106     },
47107     // private
47108     reset : function(){
47109         this.clearValue();
47110     },
47111     // private
47112     findRecord : function(prop, value){
47113         
47114         return false;
47115     
47116         var record;
47117         if(this.store.getCount() > 0){
47118             this.store.each(function(r){
47119                 if(r.data[prop] == value){
47120                     record = r;
47121                     return false;
47122                 }
47123                 return true;
47124             });
47125         }
47126         return record;
47127     },
47128     
47129     getName: function()
47130     {
47131         // returns hidden if it's set..
47132         if (!this.rendered) {return ''};
47133         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47134         
47135     },
47136      
47137
47138     
47139
47140     // private
47141     onEmptyResults : function(){
47142         Roo.log('empty results');
47143         //this.collapse();
47144     },
47145
47146     /**
47147      * Returns true if the dropdown list is expanded, else false.
47148      */
47149     isExpanded : function(){
47150         return false;
47151     },
47152
47153     /**
47154      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47156      * @param {String} value The data value of the item to select
47157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47158      * selected item if it is not currently in view (defaults to true)
47159      * @return {Boolean} True if the value matched an item in the list, else false
47160      */
47161     selectByValue : function(v, scrollIntoView){
47162         Roo.log('select By Value');
47163         return false;
47164     
47165         if(v !== undefined && v !== null){
47166             var r = this.findRecord(this.valueField || this.displayField, v);
47167             if(r){
47168                 this.select(this.store.indexOf(r), scrollIntoView);
47169                 return true;
47170             }
47171         }
47172         return false;
47173     },
47174
47175     /**
47176      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47177      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47178      * @param {Number} index The zero-based index of the list item to select
47179      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47180      * selected item if it is not currently in view (defaults to true)
47181      */
47182     select : function(index, scrollIntoView){
47183         Roo.log('select ');
47184         return  ;
47185         
47186         this.selectedIndex = index;
47187         this.view.select(index);
47188         if(scrollIntoView !== false){
47189             var el = this.view.getNode(index);
47190             if(el){
47191                 this.innerList.scrollChildIntoView(el, false);
47192             }
47193         }
47194     },
47195
47196       
47197
47198     // private
47199     validateBlur : function(){
47200         
47201         return;
47202         
47203     },
47204
47205     // private
47206     initQuery : function(){
47207         this.doQuery(this.getRawValue());
47208     },
47209
47210     // private
47211     doForce : function(){
47212         if(this.el.dom.value.length > 0){
47213             this.el.dom.value =
47214                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47215              
47216         }
47217     },
47218
47219     /**
47220      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47221      * query allowing the query action to be canceled if needed.
47222      * @param {String} query The SQL query to execute
47223      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47224      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47225      * saved in the current store (defaults to false)
47226      */
47227     doQuery : function(q, forceAll){
47228         
47229         Roo.log('doQuery?');
47230         if(q === undefined || q === null){
47231             q = '';
47232         }
47233         var qe = {
47234             query: q,
47235             forceAll: forceAll,
47236             combo: this,
47237             cancel:false
47238         };
47239         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47240             return false;
47241         }
47242         q = qe.query;
47243         forceAll = qe.forceAll;
47244         if(forceAll === true || (q.length >= this.minChars)){
47245             if(this.lastQuery != q || this.alwaysQuery){
47246                 this.lastQuery = q;
47247                 if(this.mode == 'local'){
47248                     this.selectedIndex = -1;
47249                     if(forceAll){
47250                         this.store.clearFilter();
47251                     }else{
47252                         this.store.filter(this.displayField, q);
47253                     }
47254                     this.onLoad();
47255                 }else{
47256                     this.store.baseParams[this.queryParam] = q;
47257                     this.store.load({
47258                         params: this.getParams(q)
47259                     });
47260                     this.expand();
47261                 }
47262             }else{
47263                 this.selectedIndex = -1;
47264                 this.onLoad();   
47265             }
47266         }
47267     },
47268
47269     // private
47270     getParams : function(q){
47271         var p = {};
47272         //p[this.queryParam] = q;
47273         if(this.pageSize){
47274             p.start = 0;
47275             p.limit = this.pageSize;
47276         }
47277         return p;
47278     },
47279
47280     /**
47281      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47282      */
47283     collapse : function(){
47284         
47285     },
47286
47287     // private
47288     collapseIf : function(e){
47289         
47290     },
47291
47292     /**
47293      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47294      */
47295     expand : function(){
47296         
47297     } ,
47298
47299     // private
47300      
47301
47302     /** 
47303     * @cfg {Boolean} grow 
47304     * @hide 
47305     */
47306     /** 
47307     * @cfg {Number} growMin 
47308     * @hide 
47309     */
47310     /** 
47311     * @cfg {Number} growMax 
47312     * @hide 
47313     */
47314     /**
47315      * @hide
47316      * @method autoSize
47317      */
47318     
47319     setWidth : function()
47320     {
47321         
47322     },
47323     getResizeEl : function(){
47324         return this.el;
47325     }
47326 });//<script type="text/javasscript">
47327  
47328
47329 /**
47330  * @class Roo.DDView
47331  * A DnD enabled version of Roo.View.
47332  * @param {Element/String} container The Element in which to create the View.
47333  * @param {String} tpl The template string used to create the markup for each element of the View
47334  * @param {Object} config The configuration properties. These include all the config options of
47335  * {@link Roo.View} plus some specific to this class.<br>
47336  * <p>
47337  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47338  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47339  * <p>
47340  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47341 .x-view-drag-insert-above {
47342         border-top:1px dotted #3366cc;
47343 }
47344 .x-view-drag-insert-below {
47345         border-bottom:1px dotted #3366cc;
47346 }
47347 </code></pre>
47348  * 
47349  */
47350  
47351 Roo.DDView = function(container, tpl, config) {
47352     Roo.DDView.superclass.constructor.apply(this, arguments);
47353     this.getEl().setStyle("outline", "0px none");
47354     this.getEl().unselectable();
47355     if (this.dragGroup) {
47356                 this.setDraggable(this.dragGroup.split(","));
47357     }
47358     if (this.dropGroup) {
47359                 this.setDroppable(this.dropGroup.split(","));
47360     }
47361     if (this.deletable) {
47362         this.setDeletable();
47363     }
47364     this.isDirtyFlag = false;
47365         this.addEvents({
47366                 "drop" : true
47367         });
47368 };
47369
47370 Roo.extend(Roo.DDView, Roo.View, {
47371 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47372 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47373 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47374 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47375
47376         isFormField: true,
47377
47378         reset: Roo.emptyFn,
47379         
47380         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47381
47382         validate: function() {
47383                 return true;
47384         },
47385         
47386         destroy: function() {
47387                 this.purgeListeners();
47388                 this.getEl.removeAllListeners();
47389                 this.getEl().remove();
47390                 if (this.dragZone) {
47391                         if (this.dragZone.destroy) {
47392                                 this.dragZone.destroy();
47393                         }
47394                 }
47395                 if (this.dropZone) {
47396                         if (this.dropZone.destroy) {
47397                                 this.dropZone.destroy();
47398                         }
47399                 }
47400         },
47401
47402 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47403         getName: function() {
47404                 return this.name;
47405         },
47406
47407 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47408         setValue: function(v) {
47409                 if (!this.store) {
47410                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47411                 }
47412                 var data = {};
47413                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47414                 this.store.proxy = new Roo.data.MemoryProxy(data);
47415                 this.store.load();
47416         },
47417
47418 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47419         getValue: function() {
47420                 var result = '(';
47421                 this.store.each(function(rec) {
47422                         result += rec.id + ',';
47423                 });
47424                 return result.substr(0, result.length - 1) + ')';
47425         },
47426         
47427         getIds: function() {
47428                 var i = 0, result = new Array(this.store.getCount());
47429                 this.store.each(function(rec) {
47430                         result[i++] = rec.id;
47431                 });
47432                 return result;
47433         },
47434         
47435         isDirty: function() {
47436                 return this.isDirtyFlag;
47437         },
47438
47439 /**
47440  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47441  *      whole Element becomes the target, and this causes the drop gesture to append.
47442  */
47443     getTargetFromEvent : function(e) {
47444                 var target = e.getTarget();
47445                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47446                 target = target.parentNode;
47447                 }
47448                 if (!target) {
47449                         target = this.el.dom.lastChild || this.el.dom;
47450                 }
47451                 return target;
47452     },
47453
47454 /**
47455  *      Create the drag data which consists of an object which has the property "ddel" as
47456  *      the drag proxy element. 
47457  */
47458     getDragData : function(e) {
47459         var target = this.findItemFromChild(e.getTarget());
47460                 if(target) {
47461                         this.handleSelection(e);
47462                         var selNodes = this.getSelectedNodes();
47463             var dragData = {
47464                 source: this,
47465                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47466                 nodes: selNodes,
47467                 records: []
47468                         };
47469                         var selectedIndices = this.getSelectedIndexes();
47470                         for (var i = 0; i < selectedIndices.length; i++) {
47471                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47472                         }
47473                         if (selNodes.length == 1) {
47474                                 dragData.ddel = target.cloneNode(true); // the div element
47475                         } else {
47476                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47477                                 div.className = 'multi-proxy';
47478                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47479                                         div.appendChild(selNodes[i].cloneNode(true));
47480                                 }
47481                                 dragData.ddel = div;
47482                         }
47483             //console.log(dragData)
47484             //console.log(dragData.ddel.innerHTML)
47485                         return dragData;
47486                 }
47487         //console.log('nodragData')
47488                 return false;
47489     },
47490     
47491 /**     Specify to which ddGroup items in this DDView may be dragged. */
47492     setDraggable: function(ddGroup) {
47493         if (ddGroup instanceof Array) {
47494                 Roo.each(ddGroup, this.setDraggable, this);
47495                 return;
47496         }
47497         if (this.dragZone) {
47498                 this.dragZone.addToGroup(ddGroup);
47499         } else {
47500                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47501                                 containerScroll: true,
47502                                 ddGroup: ddGroup 
47503
47504                         });
47505 //                      Draggability implies selection. DragZone's mousedown selects the element.
47506                         if (!this.multiSelect) { this.singleSelect = true; }
47507
47508 //                      Wire the DragZone's handlers up to methods in *this*
47509                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47510                 }
47511     },
47512
47513 /**     Specify from which ddGroup this DDView accepts drops. */
47514     setDroppable: function(ddGroup) {
47515         if (ddGroup instanceof Array) {
47516                 Roo.each(ddGroup, this.setDroppable, this);
47517                 return;
47518         }
47519         if (this.dropZone) {
47520                 this.dropZone.addToGroup(ddGroup);
47521         } else {
47522                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47523                                 containerScroll: true,
47524                                 ddGroup: ddGroup
47525                         });
47526
47527 //                      Wire the DropZone's handlers up to methods in *this*
47528                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47529                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47530                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47531                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47532                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47533                 }
47534     },
47535
47536 /**     Decide whether to drop above or below a View node. */
47537     getDropPoint : function(e, n, dd){
47538         if (n == this.el.dom) { return "above"; }
47539                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47540                 var c = t + (b - t) / 2;
47541                 var y = Roo.lib.Event.getPageY(e);
47542                 if(y <= c) {
47543                         return "above";
47544                 }else{
47545                         return "below";
47546                 }
47547     },
47548
47549     onNodeEnter : function(n, dd, e, data){
47550                 return false;
47551     },
47552     
47553     onNodeOver : function(n, dd, e, data){
47554                 var pt = this.getDropPoint(e, n, dd);
47555                 // set the insert point style on the target node
47556                 var dragElClass = this.dropNotAllowed;
47557                 if (pt) {
47558                         var targetElClass;
47559                         if (pt == "above"){
47560                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47561                                 targetElClass = "x-view-drag-insert-above";
47562                         } else {
47563                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47564                                 targetElClass = "x-view-drag-insert-below";
47565                         }
47566                         if (this.lastInsertClass != targetElClass){
47567                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47568                                 this.lastInsertClass = targetElClass;
47569                         }
47570                 }
47571                 return dragElClass;
47572         },
47573
47574     onNodeOut : function(n, dd, e, data){
47575                 this.removeDropIndicators(n);
47576     },
47577
47578     onNodeDrop : function(n, dd, e, data){
47579         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47580                 return false;
47581         }
47582         var pt = this.getDropPoint(e, n, dd);
47583                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47584                 if (pt == "below") { insertAt++; }
47585                 for (var i = 0; i < data.records.length; i++) {
47586                         var r = data.records[i];
47587                         var dup = this.store.getById(r.id);
47588                         if (dup && (dd != this.dragZone)) {
47589                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47590                         } else {
47591                                 if (data.copy) {
47592                                         this.store.insert(insertAt++, r.copy());
47593                                 } else {
47594                                         data.source.isDirtyFlag = true;
47595                                         r.store.remove(r);
47596                                         this.store.insert(insertAt++, r);
47597                                 }
47598                                 this.isDirtyFlag = true;
47599                         }
47600                 }
47601                 this.dragZone.cachedTarget = null;
47602                 return true;
47603     },
47604
47605     removeDropIndicators : function(n){
47606                 if(n){
47607                         Roo.fly(n).removeClass([
47608                                 "x-view-drag-insert-above",
47609                                 "x-view-drag-insert-below"]);
47610                         this.lastInsertClass = "_noclass";
47611                 }
47612     },
47613
47614 /**
47615  *      Utility method. Add a delete option to the DDView's context menu.
47616  *      @param {String} imageUrl The URL of the "delete" icon image.
47617  */
47618         setDeletable: function(imageUrl) {
47619                 if (!this.singleSelect && !this.multiSelect) {
47620                         this.singleSelect = true;
47621                 }
47622                 var c = this.getContextMenu();
47623                 this.contextMenu.on("itemclick", function(item) {
47624                         switch (item.id) {
47625                                 case "delete":
47626                                         this.remove(this.getSelectedIndexes());
47627                                         break;
47628                         }
47629                 }, this);
47630                 this.contextMenu.add({
47631                         icon: imageUrl,
47632                         id: "delete",
47633                         text: 'Delete'
47634                 });
47635         },
47636         
47637 /**     Return the context menu for this DDView. */
47638         getContextMenu: function() {
47639                 if (!this.contextMenu) {
47640 //                      Create the View's context menu
47641                         this.contextMenu = new Roo.menu.Menu({
47642                                 id: this.id + "-contextmenu"
47643                         });
47644                         this.el.on("contextmenu", this.showContextMenu, this);
47645                 }
47646                 return this.contextMenu;
47647         },
47648         
47649         disableContextMenu: function() {
47650                 if (this.contextMenu) {
47651                         this.el.un("contextmenu", this.showContextMenu, this);
47652                 }
47653         },
47654
47655         showContextMenu: function(e, item) {
47656         item = this.findItemFromChild(e.getTarget());
47657                 if (item) {
47658                         e.stopEvent();
47659                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47660                         this.contextMenu.showAt(e.getXY());
47661             }
47662     },
47663
47664 /**
47665  *      Remove {@link Roo.data.Record}s at the specified indices.
47666  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47667  */
47668     remove: function(selectedIndices) {
47669                 selectedIndices = [].concat(selectedIndices);
47670                 for (var i = 0; i < selectedIndices.length; i++) {
47671                         var rec = this.store.getAt(selectedIndices[i]);
47672                         this.store.remove(rec);
47673                 }
47674     },
47675
47676 /**
47677  *      Double click fires the event, but also, if this is draggable, and there is only one other
47678  *      related DropZone, it transfers the selected node.
47679  */
47680     onDblClick : function(e){
47681         var item = this.findItemFromChild(e.getTarget());
47682         if(item){
47683             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47684                 return false;
47685             }
47686             if (this.dragGroup) {
47687                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47688                     while (targets.indexOf(this.dropZone) > -1) {
47689                             targets.remove(this.dropZone);
47690                                 }
47691                     if (targets.length == 1) {
47692                                         this.dragZone.cachedTarget = null;
47693                         var el = Roo.get(targets[0].getEl());
47694                         var box = el.getBox(true);
47695                         targets[0].onNodeDrop(el.dom, {
47696                                 target: el.dom,
47697                                 xy: [box.x, box.y + box.height - 1]
47698                         }, null, this.getDragData(e));
47699                     }
47700                 }
47701         }
47702     },
47703     
47704     handleSelection: function(e) {
47705                 this.dragZone.cachedTarget = null;
47706         var item = this.findItemFromChild(e.getTarget());
47707         if (!item) {
47708                 this.clearSelections(true);
47709                 return;
47710         }
47711                 if (item && (this.multiSelect || this.singleSelect)){
47712                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47713                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47714                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47715                                 this.unselect(item);
47716                         } else {
47717                                 this.select(item, this.multiSelect && e.ctrlKey);
47718                                 this.lastSelection = item;
47719                         }
47720                 }
47721     },
47722
47723     onItemClick : function(item, index, e){
47724                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47725                         return false;
47726                 }
47727                 return true;
47728     },
47729
47730     unselect : function(nodeInfo, suppressEvent){
47731                 var node = this.getNode(nodeInfo);
47732                 if(node && this.isSelected(node)){
47733                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47734                                 Roo.fly(node).removeClass(this.selectedClass);
47735                                 this.selections.remove(node);
47736                                 if(!suppressEvent){
47737                                         this.fireEvent("selectionchange", this, this.selections);
47738                                 }
47739                         }
47740                 }
47741     }
47742 });
47743 /*
47744  * Based on:
47745  * Ext JS Library 1.1.1
47746  * Copyright(c) 2006-2007, Ext JS, LLC.
47747  *
47748  * Originally Released Under LGPL - original licence link has changed is not relivant.
47749  *
47750  * Fork - LGPL
47751  * <script type="text/javascript">
47752  */
47753  
47754 /**
47755  * @class Roo.LayoutManager
47756  * @extends Roo.util.Observable
47757  * Base class for layout managers.
47758  */
47759 Roo.LayoutManager = function(container, config){
47760     Roo.LayoutManager.superclass.constructor.call(this);
47761     this.el = Roo.get(container);
47762     // ie scrollbar fix
47763     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47764         document.body.scroll = "no";
47765     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47766         this.el.position('relative');
47767     }
47768     this.id = this.el.id;
47769     this.el.addClass("x-layout-container");
47770     /** false to disable window resize monitoring @type Boolean */
47771     this.monitorWindowResize = true;
47772     this.regions = {};
47773     this.addEvents({
47774         /**
47775          * @event layout
47776          * Fires when a layout is performed. 
47777          * @param {Roo.LayoutManager} this
47778          */
47779         "layout" : true,
47780         /**
47781          * @event regionresized
47782          * Fires when the user resizes a region. 
47783          * @param {Roo.LayoutRegion} region The resized region
47784          * @param {Number} newSize The new size (width for east/west, height for north/south)
47785          */
47786         "regionresized" : true,
47787         /**
47788          * @event regioncollapsed
47789          * Fires when a region is collapsed. 
47790          * @param {Roo.LayoutRegion} region The collapsed region
47791          */
47792         "regioncollapsed" : true,
47793         /**
47794          * @event regionexpanded
47795          * Fires when a region is expanded.  
47796          * @param {Roo.LayoutRegion} region The expanded region
47797          */
47798         "regionexpanded" : true
47799     });
47800     this.updating = false;
47801     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47802 };
47803
47804 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47805     /**
47806      * Returns true if this layout is currently being updated
47807      * @return {Boolean}
47808      */
47809     isUpdating : function(){
47810         return this.updating; 
47811     },
47812     
47813     /**
47814      * Suspend the LayoutManager from doing auto-layouts while
47815      * making multiple add or remove calls
47816      */
47817     beginUpdate : function(){
47818         this.updating = true;    
47819     },
47820     
47821     /**
47822      * Restore auto-layouts and optionally disable the manager from performing a layout
47823      * @param {Boolean} noLayout true to disable a layout update 
47824      */
47825     endUpdate : function(noLayout){
47826         this.updating = false;
47827         if(!noLayout){
47828             this.layout();
47829         }    
47830     },
47831     
47832     layout: function(){
47833         
47834     },
47835     
47836     onRegionResized : function(region, newSize){
47837         this.fireEvent("regionresized", region, newSize);
47838         this.layout();
47839     },
47840     
47841     onRegionCollapsed : function(region){
47842         this.fireEvent("regioncollapsed", region);
47843     },
47844     
47845     onRegionExpanded : function(region){
47846         this.fireEvent("regionexpanded", region);
47847     },
47848         
47849     /**
47850      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47851      * performs box-model adjustments.
47852      * @return {Object} The size as an object {width: (the width), height: (the height)}
47853      */
47854     getViewSize : function(){
47855         var size;
47856         if(this.el.dom != document.body){
47857             size = this.el.getSize();
47858         }else{
47859             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47860         }
47861         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47862         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47863         return size;
47864     },
47865     
47866     /**
47867      * Returns the Element this layout is bound to.
47868      * @return {Roo.Element}
47869      */
47870     getEl : function(){
47871         return this.el;
47872     },
47873     
47874     /**
47875      * Returns the specified region.
47876      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47877      * @return {Roo.LayoutRegion}
47878      */
47879     getRegion : function(target){
47880         return this.regions[target.toLowerCase()];
47881     },
47882     
47883     onWindowResize : function(){
47884         if(this.monitorWindowResize){
47885             this.layout();
47886         }
47887     }
47888 });/*
47889  * Based on:
47890  * Ext JS Library 1.1.1
47891  * Copyright(c) 2006-2007, Ext JS, LLC.
47892  *
47893  * Originally Released Under LGPL - original licence link has changed is not relivant.
47894  *
47895  * Fork - LGPL
47896  * <script type="text/javascript">
47897  */
47898 /**
47899  * @class Roo.BorderLayout
47900  * @extends Roo.LayoutManager
47901  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47902  * please see: <br><br>
47903  * <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>
47904  * <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>
47905  * Example:
47906  <pre><code>
47907  var layout = new Roo.BorderLayout(document.body, {
47908     north: {
47909         initialSize: 25,
47910         titlebar: false
47911     },
47912     west: {
47913         split:true,
47914         initialSize: 200,
47915         minSize: 175,
47916         maxSize: 400,
47917         titlebar: true,
47918         collapsible: true
47919     },
47920     east: {
47921         split:true,
47922         initialSize: 202,
47923         minSize: 175,
47924         maxSize: 400,
47925         titlebar: true,
47926         collapsible: true
47927     },
47928     south: {
47929         split:true,
47930         initialSize: 100,
47931         minSize: 100,
47932         maxSize: 200,
47933         titlebar: true,
47934         collapsible: true
47935     },
47936     center: {
47937         titlebar: true,
47938         autoScroll:true,
47939         resizeTabs: true,
47940         minTabWidth: 50,
47941         preferredTabWidth: 150
47942     }
47943 });
47944
47945 // shorthand
47946 var CP = Roo.ContentPanel;
47947
47948 layout.beginUpdate();
47949 layout.add("north", new CP("north", "North"));
47950 layout.add("south", new CP("south", {title: "South", closable: true}));
47951 layout.add("west", new CP("west", {title: "West"}));
47952 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
47953 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
47954 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
47955 layout.getRegion("center").showPanel("center1");
47956 layout.endUpdate();
47957 </code></pre>
47958
47959 <b>The container the layout is rendered into can be either the body element or any other element.
47960 If it is not the body element, the container needs to either be an absolute positioned element,
47961 or you will need to add "position:relative" to the css of the container.  You will also need to specify
47962 the container size if it is not the body element.</b>
47963
47964 * @constructor
47965 * Create a new BorderLayout
47966 * @param {String/HTMLElement/Element} container The container this layout is bound to
47967 * @param {Object} config Configuration options
47968  */
47969 Roo.BorderLayout = function(container, config){
47970     config = config || {};
47971     Roo.BorderLayout.superclass.constructor.call(this, container, config);
47972     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
47973     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
47974         var target = this.factory.validRegions[i];
47975         if(config[target]){
47976             this.addRegion(target, config[target]);
47977         }
47978     }
47979 };
47980
47981 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
47982     /**
47983      * Creates and adds a new region if it doesn't already exist.
47984      * @param {String} target The target region key (north, south, east, west or center).
47985      * @param {Object} config The regions config object
47986      * @return {BorderLayoutRegion} The new region
47987      */
47988     addRegion : function(target, config){
47989         if(!this.regions[target]){
47990             var r = this.factory.create(target, this, config);
47991             this.bindRegion(target, r);
47992         }
47993         return this.regions[target];
47994     },
47995
47996     // private (kinda)
47997     bindRegion : function(name, r){
47998         this.regions[name] = r;
47999         r.on("visibilitychange", this.layout, this);
48000         r.on("paneladded", this.layout, this);
48001         r.on("panelremoved", this.layout, this);
48002         r.on("invalidated", this.layout, this);
48003         r.on("resized", this.onRegionResized, this);
48004         r.on("collapsed", this.onRegionCollapsed, this);
48005         r.on("expanded", this.onRegionExpanded, this);
48006     },
48007
48008     /**
48009      * Performs a layout update.
48010      */
48011     layout : function(){
48012         if(this.updating) return;
48013         var size = this.getViewSize();
48014         var w = size.width;
48015         var h = size.height;
48016         var centerW = w;
48017         var centerH = h;
48018         var centerY = 0;
48019         var centerX = 0;
48020         //var x = 0, y = 0;
48021
48022         var rs = this.regions;
48023         var north = rs["north"];
48024         var south = rs["south"]; 
48025         var west = rs["west"];
48026         var east = rs["east"];
48027         var center = rs["center"];
48028         //if(this.hideOnLayout){ // not supported anymore
48029             //c.el.setStyle("display", "none");
48030         //}
48031         if(north && north.isVisible()){
48032             var b = north.getBox();
48033             var m = north.getMargins();
48034             b.width = w - (m.left+m.right);
48035             b.x = m.left;
48036             b.y = m.top;
48037             centerY = b.height + b.y + m.bottom;
48038             centerH -= centerY;
48039             north.updateBox(this.safeBox(b));
48040         }
48041         if(south && south.isVisible()){
48042             var b = south.getBox();
48043             var m = south.getMargins();
48044             b.width = w - (m.left+m.right);
48045             b.x = m.left;
48046             var totalHeight = (b.height + m.top + m.bottom);
48047             b.y = h - totalHeight + m.top;
48048             centerH -= totalHeight;
48049             south.updateBox(this.safeBox(b));
48050         }
48051         if(west && west.isVisible()){
48052             var b = west.getBox();
48053             var m = west.getMargins();
48054             b.height = centerH - (m.top+m.bottom);
48055             b.x = m.left;
48056             b.y = centerY + m.top;
48057             var totalWidth = (b.width + m.left + m.right);
48058             centerX += totalWidth;
48059             centerW -= totalWidth;
48060             west.updateBox(this.safeBox(b));
48061         }
48062         if(east && east.isVisible()){
48063             var b = east.getBox();
48064             var m = east.getMargins();
48065             b.height = centerH - (m.top+m.bottom);
48066             var totalWidth = (b.width + m.left + m.right);
48067             b.x = w - totalWidth + m.left;
48068             b.y = centerY + m.top;
48069             centerW -= totalWidth;
48070             east.updateBox(this.safeBox(b));
48071         }
48072         if(center){
48073             var m = center.getMargins();
48074             var centerBox = {
48075                 x: centerX + m.left,
48076                 y: centerY + m.top,
48077                 width: centerW - (m.left+m.right),
48078                 height: centerH - (m.top+m.bottom)
48079             };
48080             //if(this.hideOnLayout){
48081                 //center.el.setStyle("display", "block");
48082             //}
48083             center.updateBox(this.safeBox(centerBox));
48084         }
48085         this.el.repaint();
48086         this.fireEvent("layout", this);
48087     },
48088
48089     // private
48090     safeBox : function(box){
48091         box.width = Math.max(0, box.width);
48092         box.height = Math.max(0, box.height);
48093         return box;
48094     },
48095
48096     /**
48097      * Adds a ContentPanel (or subclass) to this layout.
48098      * @param {String} target The target region key (north, south, east, west or center).
48099      * @param {Roo.ContentPanel} panel The panel to add
48100      * @return {Roo.ContentPanel} The added panel
48101      */
48102     add : function(target, panel){
48103          
48104         target = target.toLowerCase();
48105         return this.regions[target].add(panel);
48106     },
48107
48108     /**
48109      * Remove a ContentPanel (or subclass) to this layout.
48110      * @param {String} target The target region key (north, south, east, west or center).
48111      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48112      * @return {Roo.ContentPanel} The removed panel
48113      */
48114     remove : function(target, panel){
48115         target = target.toLowerCase();
48116         return this.regions[target].remove(panel);
48117     },
48118
48119     /**
48120      * Searches all regions for a panel with the specified id
48121      * @param {String} panelId
48122      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48123      */
48124     findPanel : function(panelId){
48125         var rs = this.regions;
48126         for(var target in rs){
48127             if(typeof rs[target] != "function"){
48128                 var p = rs[target].getPanel(panelId);
48129                 if(p){
48130                     return p;
48131                 }
48132             }
48133         }
48134         return null;
48135     },
48136
48137     /**
48138      * Searches all regions for a panel with the specified id and activates (shows) it.
48139      * @param {String/ContentPanel} panelId The panels id or the panel itself
48140      * @return {Roo.ContentPanel} The shown panel or null
48141      */
48142     showPanel : function(panelId) {
48143       var rs = this.regions;
48144       for(var target in rs){
48145          var r = rs[target];
48146          if(typeof r != "function"){
48147             if(r.hasPanel(panelId)){
48148                return r.showPanel(panelId);
48149             }
48150          }
48151       }
48152       return null;
48153    },
48154
48155    /**
48156      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48157      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48158      */
48159     restoreState : function(provider){
48160         if(!provider){
48161             provider = Roo.state.Manager;
48162         }
48163         var sm = new Roo.LayoutStateManager();
48164         sm.init(this, provider);
48165     },
48166
48167     /**
48168      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48169      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48170      * a valid ContentPanel config object.  Example:
48171      * <pre><code>
48172 // Create the main layout
48173 var layout = new Roo.BorderLayout('main-ct', {
48174     west: {
48175         split:true,
48176         minSize: 175,
48177         titlebar: true
48178     },
48179     center: {
48180         title:'Components'
48181     }
48182 }, 'main-ct');
48183
48184 // Create and add multiple ContentPanels at once via configs
48185 layout.batchAdd({
48186    west: {
48187        id: 'source-files',
48188        autoCreate:true,
48189        title:'Ext Source Files',
48190        autoScroll:true,
48191        fitToFrame:true
48192    },
48193    center : {
48194        el: cview,
48195        autoScroll:true,
48196        fitToFrame:true,
48197        toolbar: tb,
48198        resizeEl:'cbody'
48199    }
48200 });
48201 </code></pre>
48202      * @param {Object} regions An object containing ContentPanel configs by region name
48203      */
48204     batchAdd : function(regions){
48205         this.beginUpdate();
48206         for(var rname in regions){
48207             var lr = this.regions[rname];
48208             if(lr){
48209                 this.addTypedPanels(lr, regions[rname]);
48210             }
48211         }
48212         this.endUpdate();
48213     },
48214
48215     // private
48216     addTypedPanels : function(lr, ps){
48217         if(typeof ps == 'string'){
48218             lr.add(new Roo.ContentPanel(ps));
48219         }
48220         else if(ps instanceof Array){
48221             for(var i =0, len = ps.length; i < len; i++){
48222                 this.addTypedPanels(lr, ps[i]);
48223             }
48224         }
48225         else if(!ps.events){ // raw config?
48226             var el = ps.el;
48227             delete ps.el; // prevent conflict
48228             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48229         }
48230         else {  // panel object assumed!
48231             lr.add(ps);
48232         }
48233     },
48234     /**
48235      * Adds a xtype elements to the layout.
48236      * <pre><code>
48237
48238 layout.addxtype({
48239        xtype : 'ContentPanel',
48240        region: 'west',
48241        items: [ .... ]
48242    }
48243 );
48244
48245 layout.addxtype({
48246         xtype : 'NestedLayoutPanel',
48247         region: 'west',
48248         layout: {
48249            center: { },
48250            west: { }   
48251         },
48252         items : [ ... list of content panels or nested layout panels.. ]
48253    }
48254 );
48255 </code></pre>
48256      * @param {Object} cfg Xtype definition of item to add.
48257      */
48258     addxtype : function(cfg)
48259     {
48260         // basically accepts a pannel...
48261         // can accept a layout region..!?!?
48262         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48263         
48264         if (!cfg.xtype.match(/Panel$/)) {
48265             return false;
48266         }
48267         var ret = false;
48268         
48269         if (typeof(cfg.region) == 'undefined') {
48270             Roo.log("Failed to add Panel, region was not set");
48271             Roo.log(cfg);
48272             return false;
48273         }
48274         var region = cfg.region;
48275         delete cfg.region;
48276         
48277           
48278         var xitems = [];
48279         if (cfg.items) {
48280             xitems = cfg.items;
48281             delete cfg.items;
48282         }
48283         var nb = false;
48284         
48285         switch(cfg.xtype) 
48286         {
48287             case 'ContentPanel':  // ContentPanel (el, cfg)
48288             case 'ScrollPanel':  // ContentPanel (el, cfg)
48289             case 'ViewPanel': 
48290                 if(cfg.autoCreate) {
48291                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48292                 } else {
48293                     var el = this.el.createChild();
48294                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48295                 }
48296                 
48297                 this.add(region, ret);
48298                 break;
48299             
48300             
48301             case 'TreePanel': // our new panel!
48302                 cfg.el = this.el.createChild();
48303                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48304                 this.add(region, ret);
48305                 break;
48306             
48307             case 'NestedLayoutPanel': 
48308                 // create a new Layout (which is  a Border Layout...
48309                 var el = this.el.createChild();
48310                 var clayout = cfg.layout;
48311                 delete cfg.layout;
48312                 clayout.items   = clayout.items  || [];
48313                 // replace this exitems with the clayout ones..
48314                 xitems = clayout.items;
48315                  
48316                 
48317                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48318                     cfg.background = false;
48319                 }
48320                 var layout = new Roo.BorderLayout(el, clayout);
48321                 
48322                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48323                 //console.log('adding nested layout panel '  + cfg.toSource());
48324                 this.add(region, ret);
48325                 nb = {}; /// find first...
48326                 break;
48327                 
48328             case 'GridPanel': 
48329             
48330                 // needs grid and region
48331                 
48332                 //var el = this.getRegion(region).el.createChild();
48333                 var el = this.el.createChild();
48334                 // create the grid first...
48335                 
48336                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48337                 delete cfg.grid;
48338                 if (region == 'center' && this.active ) {
48339                     cfg.background = false;
48340                 }
48341                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48342                 
48343                 this.add(region, ret);
48344                 if (cfg.background) {
48345                     ret.on('activate', function(gp) {
48346                         if (!gp.grid.rendered) {
48347                             gp.grid.render();
48348                         }
48349                     });
48350                 } else {
48351                     grid.render();
48352                 }
48353                 break;
48354            
48355            
48356            
48357                 
48358                 
48359                 
48360             default: 
48361                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48362                 return null;
48363              // GridPanel (grid, cfg)
48364             
48365         }
48366         this.beginUpdate();
48367         // add children..
48368         var region = '';
48369         var abn = {};
48370         Roo.each(xitems, function(i)  {
48371             region = nb && i.region ? i.region : false;
48372             
48373             var add = ret.addxtype(i);
48374            
48375             if (region) {
48376                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48377                 if (!i.background) {
48378                     abn[region] = nb[region] ;
48379                 }
48380             }
48381             
48382         });
48383         this.endUpdate();
48384
48385         // make the last non-background panel active..
48386         //if (nb) { Roo.log(abn); }
48387         if (nb) {
48388             
48389             for(var r in abn) {
48390                 region = this.getRegion(r);
48391                 if (region) {
48392                     // tried using nb[r], but it does not work..
48393                      
48394                     region.showPanel(abn[r]);
48395                    
48396                 }
48397             }
48398         }
48399         return ret;
48400         
48401     }
48402 });
48403
48404 /**
48405  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48406  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48407  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48408  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48409  * <pre><code>
48410 // shorthand
48411 var CP = Roo.ContentPanel;
48412
48413 var layout = Roo.BorderLayout.create({
48414     north: {
48415         initialSize: 25,
48416         titlebar: false,
48417         panels: [new CP("north", "North")]
48418     },
48419     west: {
48420         split:true,
48421         initialSize: 200,
48422         minSize: 175,
48423         maxSize: 400,
48424         titlebar: true,
48425         collapsible: true,
48426         panels: [new CP("west", {title: "West"})]
48427     },
48428     east: {
48429         split:true,
48430         initialSize: 202,
48431         minSize: 175,
48432         maxSize: 400,
48433         titlebar: true,
48434         collapsible: true,
48435         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48436     },
48437     south: {
48438         split:true,
48439         initialSize: 100,
48440         minSize: 100,
48441         maxSize: 200,
48442         titlebar: true,
48443         collapsible: true,
48444         panels: [new CP("south", {title: "South", closable: true})]
48445     },
48446     center: {
48447         titlebar: true,
48448         autoScroll:true,
48449         resizeTabs: true,
48450         minTabWidth: 50,
48451         preferredTabWidth: 150,
48452         panels: [
48453             new CP("center1", {title: "Close Me", closable: true}),
48454             new CP("center2", {title: "Center Panel", closable: false})
48455         ]
48456     }
48457 }, document.body);
48458
48459 layout.getRegion("center").showPanel("center1");
48460 </code></pre>
48461  * @param config
48462  * @param targetEl
48463  */
48464 Roo.BorderLayout.create = function(config, targetEl){
48465     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48466     layout.beginUpdate();
48467     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48468     for(var j = 0, jlen = regions.length; j < jlen; j++){
48469         var lr = regions[j];
48470         if(layout.regions[lr] && config[lr].panels){
48471             var r = layout.regions[lr];
48472             var ps = config[lr].panels;
48473             layout.addTypedPanels(r, ps);
48474         }
48475     }
48476     layout.endUpdate();
48477     return layout;
48478 };
48479
48480 // private
48481 Roo.BorderLayout.RegionFactory = {
48482     // private
48483     validRegions : ["north","south","east","west","center"],
48484
48485     // private
48486     create : function(target, mgr, config){
48487         target = target.toLowerCase();
48488         if(config.lightweight || config.basic){
48489             return new Roo.BasicLayoutRegion(mgr, config, target);
48490         }
48491         switch(target){
48492             case "north":
48493                 return new Roo.NorthLayoutRegion(mgr, config);
48494             case "south":
48495                 return new Roo.SouthLayoutRegion(mgr, config);
48496             case "east":
48497                 return new Roo.EastLayoutRegion(mgr, config);
48498             case "west":
48499                 return new Roo.WestLayoutRegion(mgr, config);
48500             case "center":
48501                 return new Roo.CenterLayoutRegion(mgr, config);
48502         }
48503         throw 'Layout region "'+target+'" not supported.';
48504     }
48505 };/*
48506  * Based on:
48507  * Ext JS Library 1.1.1
48508  * Copyright(c) 2006-2007, Ext JS, LLC.
48509  *
48510  * Originally Released Under LGPL - original licence link has changed is not relivant.
48511  *
48512  * Fork - LGPL
48513  * <script type="text/javascript">
48514  */
48515  
48516 /**
48517  * @class Roo.BasicLayoutRegion
48518  * @extends Roo.util.Observable
48519  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48520  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48521  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48522  */
48523 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48524     this.mgr = mgr;
48525     this.position  = pos;
48526     this.events = {
48527         /**
48528          * @scope Roo.BasicLayoutRegion
48529          */
48530         
48531         /**
48532          * @event beforeremove
48533          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48534          * @param {Roo.LayoutRegion} this
48535          * @param {Roo.ContentPanel} panel The panel
48536          * @param {Object} e The cancel event object
48537          */
48538         "beforeremove" : true,
48539         /**
48540          * @event invalidated
48541          * Fires when the layout for this region is changed.
48542          * @param {Roo.LayoutRegion} this
48543          */
48544         "invalidated" : true,
48545         /**
48546          * @event visibilitychange
48547          * Fires when this region is shown or hidden 
48548          * @param {Roo.LayoutRegion} this
48549          * @param {Boolean} visibility true or false
48550          */
48551         "visibilitychange" : true,
48552         /**
48553          * @event paneladded
48554          * Fires when a panel is added. 
48555          * @param {Roo.LayoutRegion} this
48556          * @param {Roo.ContentPanel} panel The panel
48557          */
48558         "paneladded" : true,
48559         /**
48560          * @event panelremoved
48561          * Fires when a panel is removed. 
48562          * @param {Roo.LayoutRegion} this
48563          * @param {Roo.ContentPanel} panel The panel
48564          */
48565         "panelremoved" : true,
48566         /**
48567          * @event collapsed
48568          * Fires when this region is collapsed.
48569          * @param {Roo.LayoutRegion} this
48570          */
48571         "collapsed" : true,
48572         /**
48573          * @event expanded
48574          * Fires when this region is expanded.
48575          * @param {Roo.LayoutRegion} this
48576          */
48577         "expanded" : true,
48578         /**
48579          * @event slideshow
48580          * Fires when this region is slid into view.
48581          * @param {Roo.LayoutRegion} this
48582          */
48583         "slideshow" : true,
48584         /**
48585          * @event slidehide
48586          * Fires when this region slides out of view. 
48587          * @param {Roo.LayoutRegion} this
48588          */
48589         "slidehide" : true,
48590         /**
48591          * @event panelactivated
48592          * Fires when a panel is activated. 
48593          * @param {Roo.LayoutRegion} this
48594          * @param {Roo.ContentPanel} panel The activated panel
48595          */
48596         "panelactivated" : true,
48597         /**
48598          * @event resized
48599          * Fires when the user resizes this region. 
48600          * @param {Roo.LayoutRegion} this
48601          * @param {Number} newSize The new size (width for east/west, height for north/south)
48602          */
48603         "resized" : true
48604     };
48605     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48606     this.panels = new Roo.util.MixedCollection();
48607     this.panels.getKey = this.getPanelId.createDelegate(this);
48608     this.box = null;
48609     this.activePanel = null;
48610     // ensure listeners are added...
48611     
48612     if (config.listeners || config.events) {
48613         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48614             listeners : config.listeners || {},
48615             events : config.events || {}
48616         });
48617     }
48618     
48619     if(skipConfig !== true){
48620         this.applyConfig(config);
48621     }
48622 };
48623
48624 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48625     getPanelId : function(p){
48626         return p.getId();
48627     },
48628     
48629     applyConfig : function(config){
48630         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48631         this.config = config;
48632         
48633     },
48634     
48635     /**
48636      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48637      * the width, for horizontal (north, south) the height.
48638      * @param {Number} newSize The new width or height
48639      */
48640     resizeTo : function(newSize){
48641         var el = this.el ? this.el :
48642                  (this.activePanel ? this.activePanel.getEl() : null);
48643         if(el){
48644             switch(this.position){
48645                 case "east":
48646                 case "west":
48647                     el.setWidth(newSize);
48648                     this.fireEvent("resized", this, newSize);
48649                 break;
48650                 case "north":
48651                 case "south":
48652                     el.setHeight(newSize);
48653                     this.fireEvent("resized", this, newSize);
48654                 break;                
48655             }
48656         }
48657     },
48658     
48659     getBox : function(){
48660         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48661     },
48662     
48663     getMargins : function(){
48664         return this.margins;
48665     },
48666     
48667     updateBox : function(box){
48668         this.box = box;
48669         var el = this.activePanel.getEl();
48670         el.dom.style.left = box.x + "px";
48671         el.dom.style.top = box.y + "px";
48672         this.activePanel.setSize(box.width, box.height);
48673     },
48674     
48675     /**
48676      * Returns the container element for this region.
48677      * @return {Roo.Element}
48678      */
48679     getEl : function(){
48680         return this.activePanel;
48681     },
48682     
48683     /**
48684      * Returns true if this region is currently visible.
48685      * @return {Boolean}
48686      */
48687     isVisible : function(){
48688         return this.activePanel ? true : false;
48689     },
48690     
48691     setActivePanel : function(panel){
48692         panel = this.getPanel(panel);
48693         if(this.activePanel && this.activePanel != panel){
48694             this.activePanel.setActiveState(false);
48695             this.activePanel.getEl().setLeftTop(-10000,-10000);
48696         }
48697         this.activePanel = panel;
48698         panel.setActiveState(true);
48699         if(this.box){
48700             panel.setSize(this.box.width, this.box.height);
48701         }
48702         this.fireEvent("panelactivated", this, panel);
48703         this.fireEvent("invalidated");
48704     },
48705     
48706     /**
48707      * Show the specified panel.
48708      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48709      * @return {Roo.ContentPanel} The shown panel or null
48710      */
48711     showPanel : function(panel){
48712         if(panel = this.getPanel(panel)){
48713             this.setActivePanel(panel);
48714         }
48715         return panel;
48716     },
48717     
48718     /**
48719      * Get the active panel for this region.
48720      * @return {Roo.ContentPanel} The active panel or null
48721      */
48722     getActivePanel : function(){
48723         return this.activePanel;
48724     },
48725     
48726     /**
48727      * Add the passed ContentPanel(s)
48728      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48729      * @return {Roo.ContentPanel} The panel added (if only one was added)
48730      */
48731     add : function(panel){
48732         if(arguments.length > 1){
48733             for(var i = 0, len = arguments.length; i < len; i++) {
48734                 this.add(arguments[i]);
48735             }
48736             return null;
48737         }
48738         if(this.hasPanel(panel)){
48739             this.showPanel(panel);
48740             return panel;
48741         }
48742         var el = panel.getEl();
48743         if(el.dom.parentNode != this.mgr.el.dom){
48744             this.mgr.el.dom.appendChild(el.dom);
48745         }
48746         if(panel.setRegion){
48747             panel.setRegion(this);
48748         }
48749         this.panels.add(panel);
48750         el.setStyle("position", "absolute");
48751         if(!panel.background){
48752             this.setActivePanel(panel);
48753             if(this.config.initialSize && this.panels.getCount()==1){
48754                 this.resizeTo(this.config.initialSize);
48755             }
48756         }
48757         this.fireEvent("paneladded", this, panel);
48758         return panel;
48759     },
48760     
48761     /**
48762      * Returns true if the panel is in this region.
48763      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48764      * @return {Boolean}
48765      */
48766     hasPanel : function(panel){
48767         if(typeof panel == "object"){ // must be panel obj
48768             panel = panel.getId();
48769         }
48770         return this.getPanel(panel) ? true : false;
48771     },
48772     
48773     /**
48774      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48775      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48776      * @param {Boolean} preservePanel Overrides the config preservePanel option
48777      * @return {Roo.ContentPanel} The panel that was removed
48778      */
48779     remove : function(panel, preservePanel){
48780         panel = this.getPanel(panel);
48781         if(!panel){
48782             return null;
48783         }
48784         var e = {};
48785         this.fireEvent("beforeremove", this, panel, e);
48786         if(e.cancel === true){
48787             return null;
48788         }
48789         var panelId = panel.getId();
48790         this.panels.removeKey(panelId);
48791         return panel;
48792     },
48793     
48794     /**
48795      * Returns the panel specified or null if it's not in this region.
48796      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48797      * @return {Roo.ContentPanel}
48798      */
48799     getPanel : function(id){
48800         if(typeof id == "object"){ // must be panel obj
48801             return id;
48802         }
48803         return this.panels.get(id);
48804     },
48805     
48806     /**
48807      * Returns this regions position (north/south/east/west/center).
48808      * @return {String} 
48809      */
48810     getPosition: function(){
48811         return this.position;    
48812     }
48813 });/*
48814  * Based on:
48815  * Ext JS Library 1.1.1
48816  * Copyright(c) 2006-2007, Ext JS, LLC.
48817  *
48818  * Originally Released Under LGPL - original licence link has changed is not relivant.
48819  *
48820  * Fork - LGPL
48821  * <script type="text/javascript">
48822  */
48823  
48824 /**
48825  * @class Roo.LayoutRegion
48826  * @extends Roo.BasicLayoutRegion
48827  * This class represents a region in a layout manager.
48828  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48829  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48830  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48831  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48832  * @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})
48833  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48834  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48835  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48836  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48837  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48838  * @cfg {String}    title           The title for the region (overrides panel titles)
48839  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48840  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48841  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48842  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48843  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48844  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48845  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48846  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48847  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48848  * @cfg {Boolean}   showPin         True to show a pin button
48849  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48850  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48851  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48852  * @cfg {Number}    width           For East/West panels
48853  * @cfg {Number}    height          For North/South panels
48854  * @cfg {Boolean}   split           To show the splitter
48855  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48856  */
48857 Roo.LayoutRegion = function(mgr, config, pos){
48858     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48859     var dh = Roo.DomHelper;
48860     /** This region's container element 
48861     * @type Roo.Element */
48862     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48863     /** This region's title element 
48864     * @type Roo.Element */
48865
48866     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48867         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48868         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48869     ]}, true);
48870     this.titleEl.enableDisplayMode();
48871     /** This region's title text element 
48872     * @type HTMLElement */
48873     this.titleTextEl = this.titleEl.dom.firstChild;
48874     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48875     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48876     this.closeBtn.enableDisplayMode();
48877     this.closeBtn.on("click", this.closeClicked, this);
48878     this.closeBtn.hide();
48879
48880     this.createBody(config);
48881     this.visible = true;
48882     this.collapsed = false;
48883
48884     if(config.hideWhenEmpty){
48885         this.hide();
48886         this.on("paneladded", this.validateVisibility, this);
48887         this.on("panelremoved", this.validateVisibility, this);
48888     }
48889     this.applyConfig(config);
48890 };
48891
48892 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48893
48894     createBody : function(){
48895         /** This region's body element 
48896         * @type Roo.Element */
48897         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48898     },
48899
48900     applyConfig : function(c){
48901         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48902             var dh = Roo.DomHelper;
48903             if(c.titlebar !== false){
48904                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48905                 this.collapseBtn.on("click", this.collapse, this);
48906                 this.collapseBtn.enableDisplayMode();
48907
48908                 if(c.showPin === true || this.showPin){
48909                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48910                     this.stickBtn.enableDisplayMode();
48911                     this.stickBtn.on("click", this.expand, this);
48912                     this.stickBtn.hide();
48913                 }
48914             }
48915             /** This region's collapsed element
48916             * @type Roo.Element */
48917             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48918                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48919             ]}, true);
48920             if(c.floatable !== false){
48921                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48922                this.collapsedEl.on("click", this.collapseClick, this);
48923             }
48924
48925             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48926                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48927                    id: "message", unselectable: "on", style:{"float":"left"}});
48928                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48929              }
48930             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48931             this.expandBtn.on("click", this.expand, this);
48932         }
48933         if(this.collapseBtn){
48934             this.collapseBtn.setVisible(c.collapsible == true);
48935         }
48936         this.cmargins = c.cmargins || this.cmargins ||
48937                          (this.position == "west" || this.position == "east" ?
48938                              {top: 0, left: 2, right:2, bottom: 0} :
48939                              {top: 2, left: 0, right:0, bottom: 2});
48940         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48941         this.bottomTabs = c.tabPosition != "top";
48942         this.autoScroll = c.autoScroll || false;
48943         if(this.autoScroll){
48944             this.bodyEl.setStyle("overflow", "auto");
48945         }else{
48946             this.bodyEl.setStyle("overflow", "hidden");
48947         }
48948         //if(c.titlebar !== false){
48949             if((!c.titlebar && !c.title) || c.titlebar === false){
48950                 this.titleEl.hide();
48951             }else{
48952                 this.titleEl.show();
48953                 if(c.title){
48954                     this.titleTextEl.innerHTML = c.title;
48955                 }
48956             }
48957         //}
48958         this.duration = c.duration || .30;
48959         this.slideDuration = c.slideDuration || .45;
48960         this.config = c;
48961         if(c.collapsed){
48962             this.collapse(true);
48963         }
48964         if(c.hidden){
48965             this.hide();
48966         }
48967     },
48968     /**
48969      * Returns true if this region is currently visible.
48970      * @return {Boolean}
48971      */
48972     isVisible : function(){
48973         return this.visible;
48974     },
48975
48976     /**
48977      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
48978      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
48979      */
48980     setCollapsedTitle : function(title){
48981         title = title || "&#160;";
48982         if(this.collapsedTitleTextEl){
48983             this.collapsedTitleTextEl.innerHTML = title;
48984         }
48985     },
48986
48987     getBox : function(){
48988         var b;
48989         if(!this.collapsed){
48990             b = this.el.getBox(false, true);
48991         }else{
48992             b = this.collapsedEl.getBox(false, true);
48993         }
48994         return b;
48995     },
48996
48997     getMargins : function(){
48998         return this.collapsed ? this.cmargins : this.margins;
48999     },
49000
49001     highlight : function(){
49002         this.el.addClass("x-layout-panel-dragover");
49003     },
49004
49005     unhighlight : function(){
49006         this.el.removeClass("x-layout-panel-dragover");
49007     },
49008
49009     updateBox : function(box){
49010         this.box = box;
49011         if(!this.collapsed){
49012             this.el.dom.style.left = box.x + "px";
49013             this.el.dom.style.top = box.y + "px";
49014             this.updateBody(box.width, box.height);
49015         }else{
49016             this.collapsedEl.dom.style.left = box.x + "px";
49017             this.collapsedEl.dom.style.top = box.y + "px";
49018             this.collapsedEl.setSize(box.width, box.height);
49019         }
49020         if(this.tabs){
49021             this.tabs.autoSizeTabs();
49022         }
49023     },
49024
49025     updateBody : function(w, h){
49026         if(w !== null){
49027             this.el.setWidth(w);
49028             w -= this.el.getBorderWidth("rl");
49029             if(this.config.adjustments){
49030                 w += this.config.adjustments[0];
49031             }
49032         }
49033         if(h !== null){
49034             this.el.setHeight(h);
49035             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49036             h -= this.el.getBorderWidth("tb");
49037             if(this.config.adjustments){
49038                 h += this.config.adjustments[1];
49039             }
49040             this.bodyEl.setHeight(h);
49041             if(this.tabs){
49042                 h = this.tabs.syncHeight(h);
49043             }
49044         }
49045         if(this.panelSize){
49046             w = w !== null ? w : this.panelSize.width;
49047             h = h !== null ? h : this.panelSize.height;
49048         }
49049         if(this.activePanel){
49050             var el = this.activePanel.getEl();
49051             w = w !== null ? w : el.getWidth();
49052             h = h !== null ? h : el.getHeight();
49053             this.panelSize = {width: w, height: h};
49054             this.activePanel.setSize(w, h);
49055         }
49056         if(Roo.isIE && this.tabs){
49057             this.tabs.el.repaint();
49058         }
49059     },
49060
49061     /**
49062      * Returns the container element for this region.
49063      * @return {Roo.Element}
49064      */
49065     getEl : function(){
49066         return this.el;
49067     },
49068
49069     /**
49070      * Hides this region.
49071      */
49072     hide : function(){
49073         if(!this.collapsed){
49074             this.el.dom.style.left = "-2000px";
49075             this.el.hide();
49076         }else{
49077             this.collapsedEl.dom.style.left = "-2000px";
49078             this.collapsedEl.hide();
49079         }
49080         this.visible = false;
49081         this.fireEvent("visibilitychange", this, false);
49082     },
49083
49084     /**
49085      * Shows this region if it was previously hidden.
49086      */
49087     show : function(){
49088         if(!this.collapsed){
49089             this.el.show();
49090         }else{
49091             this.collapsedEl.show();
49092         }
49093         this.visible = true;
49094         this.fireEvent("visibilitychange", this, true);
49095     },
49096
49097     closeClicked : function(){
49098         if(this.activePanel){
49099             this.remove(this.activePanel);
49100         }
49101     },
49102
49103     collapseClick : function(e){
49104         if(this.isSlid){
49105            e.stopPropagation();
49106            this.slideIn();
49107         }else{
49108            e.stopPropagation();
49109            this.slideOut();
49110         }
49111     },
49112
49113     /**
49114      * Collapses this region.
49115      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49116      */
49117     collapse : function(skipAnim){
49118         if(this.collapsed) return;
49119         this.collapsed = true;
49120         if(this.split){
49121             this.split.el.hide();
49122         }
49123         if(this.config.animate && skipAnim !== true){
49124             this.fireEvent("invalidated", this);
49125             this.animateCollapse();
49126         }else{
49127             this.el.setLocation(-20000,-20000);
49128             this.el.hide();
49129             this.collapsedEl.show();
49130             this.fireEvent("collapsed", this);
49131             this.fireEvent("invalidated", this);
49132         }
49133     },
49134
49135     animateCollapse : function(){
49136         // overridden
49137     },
49138
49139     /**
49140      * Expands this region if it was previously collapsed.
49141      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49142      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49143      */
49144     expand : function(e, skipAnim){
49145         if(e) e.stopPropagation();
49146         if(!this.collapsed || this.el.hasActiveFx()) return;
49147         if(this.isSlid){
49148             this.afterSlideIn();
49149             skipAnim = true;
49150         }
49151         this.collapsed = false;
49152         if(this.config.animate && skipAnim !== true){
49153             this.animateExpand();
49154         }else{
49155             this.el.show();
49156             if(this.split){
49157                 this.split.el.show();
49158             }
49159             this.collapsedEl.setLocation(-2000,-2000);
49160             this.collapsedEl.hide();
49161             this.fireEvent("invalidated", this);
49162             this.fireEvent("expanded", this);
49163         }
49164     },
49165
49166     animateExpand : function(){
49167         // overridden
49168     },
49169
49170     initTabs : function()
49171     {
49172         this.bodyEl.setStyle("overflow", "hidden");
49173         var ts = new Roo.TabPanel(
49174                 this.bodyEl.dom,
49175                 {
49176                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49177                     disableTooltips: this.config.disableTabTips,
49178                     toolbar : this.config.toolbar
49179                 }
49180         );
49181         if(this.config.hideTabs){
49182             ts.stripWrap.setDisplayed(false);
49183         }
49184         this.tabs = ts;
49185         ts.resizeTabs = this.config.resizeTabs === true;
49186         ts.minTabWidth = this.config.minTabWidth || 40;
49187         ts.maxTabWidth = this.config.maxTabWidth || 250;
49188         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49189         ts.monitorResize = false;
49190         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49191         ts.bodyEl.addClass('x-layout-tabs-body');
49192         this.panels.each(this.initPanelAsTab, this);
49193     },
49194
49195     initPanelAsTab : function(panel){
49196         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49197                     this.config.closeOnTab && panel.isClosable());
49198         if(panel.tabTip !== undefined){
49199             ti.setTooltip(panel.tabTip);
49200         }
49201         ti.on("activate", function(){
49202               this.setActivePanel(panel);
49203         }, this);
49204         if(this.config.closeOnTab){
49205             ti.on("beforeclose", function(t, e){
49206                 e.cancel = true;
49207                 this.remove(panel);
49208             }, this);
49209         }
49210         return ti;
49211     },
49212
49213     updatePanelTitle : function(panel, title){
49214         if(this.activePanel == panel){
49215             this.updateTitle(title);
49216         }
49217         if(this.tabs){
49218             var ti = this.tabs.getTab(panel.getEl().id);
49219             ti.setText(title);
49220             if(panel.tabTip !== undefined){
49221                 ti.setTooltip(panel.tabTip);
49222             }
49223         }
49224     },
49225
49226     updateTitle : function(title){
49227         if(this.titleTextEl && !this.config.title){
49228             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49229         }
49230     },
49231
49232     setActivePanel : function(panel){
49233         panel = this.getPanel(panel);
49234         if(this.activePanel && this.activePanel != panel){
49235             this.activePanel.setActiveState(false);
49236         }
49237         this.activePanel = panel;
49238         panel.setActiveState(true);
49239         if(this.panelSize){
49240             panel.setSize(this.panelSize.width, this.panelSize.height);
49241         }
49242         if(this.closeBtn){
49243             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49244         }
49245         this.updateTitle(panel.getTitle());
49246         if(this.tabs){
49247             this.fireEvent("invalidated", this);
49248         }
49249         this.fireEvent("panelactivated", this, panel);
49250     },
49251
49252     /**
49253      * Shows the specified panel.
49254      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49255      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49256      */
49257     showPanel : function(panel){
49258         if(panel = this.getPanel(panel)){
49259             if(this.tabs){
49260                 var tab = this.tabs.getTab(panel.getEl().id);
49261                 if(tab.isHidden()){
49262                     this.tabs.unhideTab(tab.id);
49263                 }
49264                 tab.activate();
49265             }else{
49266                 this.setActivePanel(panel);
49267             }
49268         }
49269         return panel;
49270     },
49271
49272     /**
49273      * Get the active panel for this region.
49274      * @return {Roo.ContentPanel} The active panel or null
49275      */
49276     getActivePanel : function(){
49277         return this.activePanel;
49278     },
49279
49280     validateVisibility : function(){
49281         if(this.panels.getCount() < 1){
49282             this.updateTitle("&#160;");
49283             this.closeBtn.hide();
49284             this.hide();
49285         }else{
49286             if(!this.isVisible()){
49287                 this.show();
49288             }
49289         }
49290     },
49291
49292     /**
49293      * Adds the passed ContentPanel(s) to this region.
49294      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49295      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49296      */
49297     add : function(panel){
49298         if(arguments.length > 1){
49299             for(var i = 0, len = arguments.length; i < len; i++) {
49300                 this.add(arguments[i]);
49301             }
49302             return null;
49303         }
49304         if(this.hasPanel(panel)){
49305             this.showPanel(panel);
49306             return panel;
49307         }
49308         panel.setRegion(this);
49309         this.panels.add(panel);
49310         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49311             this.bodyEl.dom.appendChild(panel.getEl().dom);
49312             if(panel.background !== true){
49313                 this.setActivePanel(panel);
49314             }
49315             this.fireEvent("paneladded", this, panel);
49316             return panel;
49317         }
49318         if(!this.tabs){
49319             this.initTabs();
49320         }else{
49321             this.initPanelAsTab(panel);
49322         }
49323         if(panel.background !== true){
49324             this.tabs.activate(panel.getEl().id);
49325         }
49326         this.fireEvent("paneladded", this, panel);
49327         return panel;
49328     },
49329
49330     /**
49331      * Hides the tab for the specified panel.
49332      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49333      */
49334     hidePanel : function(panel){
49335         if(this.tabs && (panel = this.getPanel(panel))){
49336             this.tabs.hideTab(panel.getEl().id);
49337         }
49338     },
49339
49340     /**
49341      * Unhides the tab for a previously hidden panel.
49342      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49343      */
49344     unhidePanel : function(panel){
49345         if(this.tabs && (panel = this.getPanel(panel))){
49346             this.tabs.unhideTab(panel.getEl().id);
49347         }
49348     },
49349
49350     clearPanels : function(){
49351         while(this.panels.getCount() > 0){
49352              this.remove(this.panels.first());
49353         }
49354     },
49355
49356     /**
49357      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49358      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49359      * @param {Boolean} preservePanel Overrides the config preservePanel option
49360      * @return {Roo.ContentPanel} The panel that was removed
49361      */
49362     remove : function(panel, preservePanel){
49363         panel = this.getPanel(panel);
49364         if(!panel){
49365             return null;
49366         }
49367         var e = {};
49368         this.fireEvent("beforeremove", this, panel, e);
49369         if(e.cancel === true){
49370             return null;
49371         }
49372         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49373         var panelId = panel.getId();
49374         this.panels.removeKey(panelId);
49375         if(preservePanel){
49376             document.body.appendChild(panel.getEl().dom);
49377         }
49378         if(this.tabs){
49379             this.tabs.removeTab(panel.getEl().id);
49380         }else if (!preservePanel){
49381             this.bodyEl.dom.removeChild(panel.getEl().dom);
49382         }
49383         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49384             var p = this.panels.first();
49385             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49386             tempEl.appendChild(p.getEl().dom);
49387             this.bodyEl.update("");
49388             this.bodyEl.dom.appendChild(p.getEl().dom);
49389             tempEl = null;
49390             this.updateTitle(p.getTitle());
49391             this.tabs = null;
49392             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49393             this.setActivePanel(p);
49394         }
49395         panel.setRegion(null);
49396         if(this.activePanel == panel){
49397             this.activePanel = null;
49398         }
49399         if(this.config.autoDestroy !== false && preservePanel !== true){
49400             try{panel.destroy();}catch(e){}
49401         }
49402         this.fireEvent("panelremoved", this, panel);
49403         return panel;
49404     },
49405
49406     /**
49407      * Returns the TabPanel component used by this region
49408      * @return {Roo.TabPanel}
49409      */
49410     getTabs : function(){
49411         return this.tabs;
49412     },
49413
49414     createTool : function(parentEl, className){
49415         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49416             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49417         btn.addClassOnOver("x-layout-tools-button-over");
49418         return btn;
49419     }
49420 });/*
49421  * Based on:
49422  * Ext JS Library 1.1.1
49423  * Copyright(c) 2006-2007, Ext JS, LLC.
49424  *
49425  * Originally Released Under LGPL - original licence link has changed is not relivant.
49426  *
49427  * Fork - LGPL
49428  * <script type="text/javascript">
49429  */
49430  
49431
49432
49433 /**
49434  * @class Roo.SplitLayoutRegion
49435  * @extends Roo.LayoutRegion
49436  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49437  */
49438 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49439     this.cursor = cursor;
49440     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49441 };
49442
49443 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49444     splitTip : "Drag to resize.",
49445     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49446     useSplitTips : false,
49447
49448     applyConfig : function(config){
49449         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49450         if(config.split){
49451             if(!this.split){
49452                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49453                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49454                 /** The SplitBar for this region 
49455                 * @type Roo.SplitBar */
49456                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49457                 this.split.on("moved", this.onSplitMove, this);
49458                 this.split.useShim = config.useShim === true;
49459                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49460                 if(this.useSplitTips){
49461                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49462                 }
49463                 if(config.collapsible){
49464                     this.split.el.on("dblclick", this.collapse,  this);
49465                 }
49466             }
49467             if(typeof config.minSize != "undefined"){
49468                 this.split.minSize = config.minSize;
49469             }
49470             if(typeof config.maxSize != "undefined"){
49471                 this.split.maxSize = config.maxSize;
49472             }
49473             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49474                 this.hideSplitter();
49475             }
49476         }
49477     },
49478
49479     getHMaxSize : function(){
49480          var cmax = this.config.maxSize || 10000;
49481          var center = this.mgr.getRegion("center");
49482          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49483     },
49484
49485     getVMaxSize : function(){
49486          var cmax = this.config.maxSize || 10000;
49487          var center = this.mgr.getRegion("center");
49488          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49489     },
49490
49491     onSplitMove : function(split, newSize){
49492         this.fireEvent("resized", this, newSize);
49493     },
49494     
49495     /** 
49496      * Returns the {@link Roo.SplitBar} for this region.
49497      * @return {Roo.SplitBar}
49498      */
49499     getSplitBar : function(){
49500         return this.split;
49501     },
49502     
49503     hide : function(){
49504         this.hideSplitter();
49505         Roo.SplitLayoutRegion.superclass.hide.call(this);
49506     },
49507
49508     hideSplitter : function(){
49509         if(this.split){
49510             this.split.el.setLocation(-2000,-2000);
49511             this.split.el.hide();
49512         }
49513     },
49514
49515     show : function(){
49516         if(this.split){
49517             this.split.el.show();
49518         }
49519         Roo.SplitLayoutRegion.superclass.show.call(this);
49520     },
49521     
49522     beforeSlide: function(){
49523         if(Roo.isGecko){// firefox overflow auto bug workaround
49524             this.bodyEl.clip();
49525             if(this.tabs) this.tabs.bodyEl.clip();
49526             if(this.activePanel){
49527                 this.activePanel.getEl().clip();
49528                 
49529                 if(this.activePanel.beforeSlide){
49530                     this.activePanel.beforeSlide();
49531                 }
49532             }
49533         }
49534     },
49535     
49536     afterSlide : function(){
49537         if(Roo.isGecko){// firefox overflow auto bug workaround
49538             this.bodyEl.unclip();
49539             if(this.tabs) this.tabs.bodyEl.unclip();
49540             if(this.activePanel){
49541                 this.activePanel.getEl().unclip();
49542                 if(this.activePanel.afterSlide){
49543                     this.activePanel.afterSlide();
49544                 }
49545             }
49546         }
49547     },
49548
49549     initAutoHide : function(){
49550         if(this.autoHide !== false){
49551             if(!this.autoHideHd){
49552                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49553                 this.autoHideHd = {
49554                     "mouseout": function(e){
49555                         if(!e.within(this.el, true)){
49556                             st.delay(500);
49557                         }
49558                     },
49559                     "mouseover" : function(e){
49560                         st.cancel();
49561                     },
49562                     scope : this
49563                 };
49564             }
49565             this.el.on(this.autoHideHd);
49566         }
49567     },
49568
49569     clearAutoHide : function(){
49570         if(this.autoHide !== false){
49571             this.el.un("mouseout", this.autoHideHd.mouseout);
49572             this.el.un("mouseover", this.autoHideHd.mouseover);
49573         }
49574     },
49575
49576     clearMonitor : function(){
49577         Roo.get(document).un("click", this.slideInIf, this);
49578     },
49579
49580     // these names are backwards but not changed for compat
49581     slideOut : function(){
49582         if(this.isSlid || this.el.hasActiveFx()){
49583             return;
49584         }
49585         this.isSlid = true;
49586         if(this.collapseBtn){
49587             this.collapseBtn.hide();
49588         }
49589         this.closeBtnState = this.closeBtn.getStyle('display');
49590         this.closeBtn.hide();
49591         if(this.stickBtn){
49592             this.stickBtn.show();
49593         }
49594         this.el.show();
49595         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49596         this.beforeSlide();
49597         this.el.setStyle("z-index", 10001);
49598         this.el.slideIn(this.getSlideAnchor(), {
49599             callback: function(){
49600                 this.afterSlide();
49601                 this.initAutoHide();
49602                 Roo.get(document).on("click", this.slideInIf, this);
49603                 this.fireEvent("slideshow", this);
49604             },
49605             scope: this,
49606             block: true
49607         });
49608     },
49609
49610     afterSlideIn : function(){
49611         this.clearAutoHide();
49612         this.isSlid = false;
49613         this.clearMonitor();
49614         this.el.setStyle("z-index", "");
49615         if(this.collapseBtn){
49616             this.collapseBtn.show();
49617         }
49618         this.closeBtn.setStyle('display', this.closeBtnState);
49619         if(this.stickBtn){
49620             this.stickBtn.hide();
49621         }
49622         this.fireEvent("slidehide", this);
49623     },
49624
49625     slideIn : function(cb){
49626         if(!this.isSlid || this.el.hasActiveFx()){
49627             Roo.callback(cb);
49628             return;
49629         }
49630         this.isSlid = false;
49631         this.beforeSlide();
49632         this.el.slideOut(this.getSlideAnchor(), {
49633             callback: function(){
49634                 this.el.setLeftTop(-10000, -10000);
49635                 this.afterSlide();
49636                 this.afterSlideIn();
49637                 Roo.callback(cb);
49638             },
49639             scope: this,
49640             block: true
49641         });
49642     },
49643     
49644     slideInIf : function(e){
49645         if(!e.within(this.el)){
49646             this.slideIn();
49647         }
49648     },
49649
49650     animateCollapse : function(){
49651         this.beforeSlide();
49652         this.el.setStyle("z-index", 20000);
49653         var anchor = this.getSlideAnchor();
49654         this.el.slideOut(anchor, {
49655             callback : function(){
49656                 this.el.setStyle("z-index", "");
49657                 this.collapsedEl.slideIn(anchor, {duration:.3});
49658                 this.afterSlide();
49659                 this.el.setLocation(-10000,-10000);
49660                 this.el.hide();
49661                 this.fireEvent("collapsed", this);
49662             },
49663             scope: this,
49664             block: true
49665         });
49666     },
49667
49668     animateExpand : function(){
49669         this.beforeSlide();
49670         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49671         this.el.setStyle("z-index", 20000);
49672         this.collapsedEl.hide({
49673             duration:.1
49674         });
49675         this.el.slideIn(this.getSlideAnchor(), {
49676             callback : function(){
49677                 this.el.setStyle("z-index", "");
49678                 this.afterSlide();
49679                 if(this.split){
49680                     this.split.el.show();
49681                 }
49682                 this.fireEvent("invalidated", this);
49683                 this.fireEvent("expanded", this);
49684             },
49685             scope: this,
49686             block: true
49687         });
49688     },
49689
49690     anchors : {
49691         "west" : "left",
49692         "east" : "right",
49693         "north" : "top",
49694         "south" : "bottom"
49695     },
49696
49697     sanchors : {
49698         "west" : "l",
49699         "east" : "r",
49700         "north" : "t",
49701         "south" : "b"
49702     },
49703
49704     canchors : {
49705         "west" : "tl-tr",
49706         "east" : "tr-tl",
49707         "north" : "tl-bl",
49708         "south" : "bl-tl"
49709     },
49710
49711     getAnchor : function(){
49712         return this.anchors[this.position];
49713     },
49714
49715     getCollapseAnchor : function(){
49716         return this.canchors[this.position];
49717     },
49718
49719     getSlideAnchor : function(){
49720         return this.sanchors[this.position];
49721     },
49722
49723     getAlignAdj : function(){
49724         var cm = this.cmargins;
49725         switch(this.position){
49726             case "west":
49727                 return [0, 0];
49728             break;
49729             case "east":
49730                 return [0, 0];
49731             break;
49732             case "north":
49733                 return [0, 0];
49734             break;
49735             case "south":
49736                 return [0, 0];
49737             break;
49738         }
49739     },
49740
49741     getExpandAdj : function(){
49742         var c = this.collapsedEl, cm = this.cmargins;
49743         switch(this.position){
49744             case "west":
49745                 return [-(cm.right+c.getWidth()+cm.left), 0];
49746             break;
49747             case "east":
49748                 return [cm.right+c.getWidth()+cm.left, 0];
49749             break;
49750             case "north":
49751                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49752             break;
49753             case "south":
49754                 return [0, cm.top+cm.bottom+c.getHeight()];
49755             break;
49756         }
49757     }
49758 });/*
49759  * Based on:
49760  * Ext JS Library 1.1.1
49761  * Copyright(c) 2006-2007, Ext JS, LLC.
49762  *
49763  * Originally Released Under LGPL - original licence link has changed is not relivant.
49764  *
49765  * Fork - LGPL
49766  * <script type="text/javascript">
49767  */
49768 /*
49769  * These classes are private internal classes
49770  */
49771 Roo.CenterLayoutRegion = function(mgr, config){
49772     Roo.LayoutRegion.call(this, mgr, config, "center");
49773     this.visible = true;
49774     this.minWidth = config.minWidth || 20;
49775     this.minHeight = config.minHeight || 20;
49776 };
49777
49778 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49779     hide : function(){
49780         // center panel can't be hidden
49781     },
49782     
49783     show : function(){
49784         // center panel can't be hidden
49785     },
49786     
49787     getMinWidth: function(){
49788         return this.minWidth;
49789     },
49790     
49791     getMinHeight: function(){
49792         return this.minHeight;
49793     }
49794 });
49795
49796
49797 Roo.NorthLayoutRegion = function(mgr, config){
49798     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49799     if(this.split){
49800         this.split.placement = Roo.SplitBar.TOP;
49801         this.split.orientation = Roo.SplitBar.VERTICAL;
49802         this.split.el.addClass("x-layout-split-v");
49803     }
49804     var size = config.initialSize || config.height;
49805     if(typeof size != "undefined"){
49806         this.el.setHeight(size);
49807     }
49808 };
49809 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49810     orientation: Roo.SplitBar.VERTICAL,
49811     getBox : function(){
49812         if(this.collapsed){
49813             return this.collapsedEl.getBox();
49814         }
49815         var box = this.el.getBox();
49816         if(this.split){
49817             box.height += this.split.el.getHeight();
49818         }
49819         return box;
49820     },
49821     
49822     updateBox : function(box){
49823         if(this.split && !this.collapsed){
49824             box.height -= this.split.el.getHeight();
49825             this.split.el.setLeft(box.x);
49826             this.split.el.setTop(box.y+box.height);
49827             this.split.el.setWidth(box.width);
49828         }
49829         if(this.collapsed){
49830             this.updateBody(box.width, null);
49831         }
49832         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49833     }
49834 });
49835
49836 Roo.SouthLayoutRegion = function(mgr, config){
49837     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49838     if(this.split){
49839         this.split.placement = Roo.SplitBar.BOTTOM;
49840         this.split.orientation = Roo.SplitBar.VERTICAL;
49841         this.split.el.addClass("x-layout-split-v");
49842     }
49843     var size = config.initialSize || config.height;
49844     if(typeof size != "undefined"){
49845         this.el.setHeight(size);
49846     }
49847 };
49848 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49849     orientation: Roo.SplitBar.VERTICAL,
49850     getBox : function(){
49851         if(this.collapsed){
49852             return this.collapsedEl.getBox();
49853         }
49854         var box = this.el.getBox();
49855         if(this.split){
49856             var sh = this.split.el.getHeight();
49857             box.height += sh;
49858             box.y -= sh;
49859         }
49860         return box;
49861     },
49862     
49863     updateBox : function(box){
49864         if(this.split && !this.collapsed){
49865             var sh = this.split.el.getHeight();
49866             box.height -= sh;
49867             box.y += sh;
49868             this.split.el.setLeft(box.x);
49869             this.split.el.setTop(box.y-sh);
49870             this.split.el.setWidth(box.width);
49871         }
49872         if(this.collapsed){
49873             this.updateBody(box.width, null);
49874         }
49875         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49876     }
49877 });
49878
49879 Roo.EastLayoutRegion = function(mgr, config){
49880     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49881     if(this.split){
49882         this.split.placement = Roo.SplitBar.RIGHT;
49883         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49884         this.split.el.addClass("x-layout-split-h");
49885     }
49886     var size = config.initialSize || config.width;
49887     if(typeof size != "undefined"){
49888         this.el.setWidth(size);
49889     }
49890 };
49891 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49892     orientation: Roo.SplitBar.HORIZONTAL,
49893     getBox : function(){
49894         if(this.collapsed){
49895             return this.collapsedEl.getBox();
49896         }
49897         var box = this.el.getBox();
49898         if(this.split){
49899             var sw = this.split.el.getWidth();
49900             box.width += sw;
49901             box.x -= sw;
49902         }
49903         return box;
49904     },
49905
49906     updateBox : function(box){
49907         if(this.split && !this.collapsed){
49908             var sw = this.split.el.getWidth();
49909             box.width -= sw;
49910             this.split.el.setLeft(box.x);
49911             this.split.el.setTop(box.y);
49912             this.split.el.setHeight(box.height);
49913             box.x += sw;
49914         }
49915         if(this.collapsed){
49916             this.updateBody(null, box.height);
49917         }
49918         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49919     }
49920 });
49921
49922 Roo.WestLayoutRegion = function(mgr, config){
49923     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49924     if(this.split){
49925         this.split.placement = Roo.SplitBar.LEFT;
49926         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49927         this.split.el.addClass("x-layout-split-h");
49928     }
49929     var size = config.initialSize || config.width;
49930     if(typeof size != "undefined"){
49931         this.el.setWidth(size);
49932     }
49933 };
49934 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49935     orientation: Roo.SplitBar.HORIZONTAL,
49936     getBox : function(){
49937         if(this.collapsed){
49938             return this.collapsedEl.getBox();
49939         }
49940         var box = this.el.getBox();
49941         if(this.split){
49942             box.width += this.split.el.getWidth();
49943         }
49944         return box;
49945     },
49946     
49947     updateBox : function(box){
49948         if(this.split && !this.collapsed){
49949             var sw = this.split.el.getWidth();
49950             box.width -= sw;
49951             this.split.el.setLeft(box.x+box.width);
49952             this.split.el.setTop(box.y);
49953             this.split.el.setHeight(box.height);
49954         }
49955         if(this.collapsed){
49956             this.updateBody(null, box.height);
49957         }
49958         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49959     }
49960 });
49961 /*
49962  * Based on:
49963  * Ext JS Library 1.1.1
49964  * Copyright(c) 2006-2007, Ext JS, LLC.
49965  *
49966  * Originally Released Under LGPL - original licence link has changed is not relivant.
49967  *
49968  * Fork - LGPL
49969  * <script type="text/javascript">
49970  */
49971  
49972  
49973 /*
49974  * Private internal class for reading and applying state
49975  */
49976 Roo.LayoutStateManager = function(layout){
49977      // default empty state
49978      this.state = {
49979         north: {},
49980         south: {},
49981         east: {},
49982         west: {}       
49983     };
49984 };
49985
49986 Roo.LayoutStateManager.prototype = {
49987     init : function(layout, provider){
49988         this.provider = provider;
49989         var state = provider.get(layout.id+"-layout-state");
49990         if(state){
49991             var wasUpdating = layout.isUpdating();
49992             if(!wasUpdating){
49993                 layout.beginUpdate();
49994             }
49995             for(var key in state){
49996                 if(typeof state[key] != "function"){
49997                     var rstate = state[key];
49998                     var r = layout.getRegion(key);
49999                     if(r && rstate){
50000                         if(rstate.size){
50001                             r.resizeTo(rstate.size);
50002                         }
50003                         if(rstate.collapsed == true){
50004                             r.collapse(true);
50005                         }else{
50006                             r.expand(null, true);
50007                         }
50008                     }
50009                 }
50010             }
50011             if(!wasUpdating){
50012                 layout.endUpdate();
50013             }
50014             this.state = state; 
50015         }
50016         this.layout = layout;
50017         layout.on("regionresized", this.onRegionResized, this);
50018         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50019         layout.on("regionexpanded", this.onRegionExpanded, this);
50020     },
50021     
50022     storeState : function(){
50023         this.provider.set(this.layout.id+"-layout-state", this.state);
50024     },
50025     
50026     onRegionResized : function(region, newSize){
50027         this.state[region.getPosition()].size = newSize;
50028         this.storeState();
50029     },
50030     
50031     onRegionCollapsed : function(region){
50032         this.state[region.getPosition()].collapsed = true;
50033         this.storeState();
50034     },
50035     
50036     onRegionExpanded : function(region){
50037         this.state[region.getPosition()].collapsed = false;
50038         this.storeState();
50039     }
50040 };/*
50041  * Based on:
50042  * Ext JS Library 1.1.1
50043  * Copyright(c) 2006-2007, Ext JS, LLC.
50044  *
50045  * Originally Released Under LGPL - original licence link has changed is not relivant.
50046  *
50047  * Fork - LGPL
50048  * <script type="text/javascript">
50049  */
50050 /**
50051  * @class Roo.ContentPanel
50052  * @extends Roo.util.Observable
50053  * A basic ContentPanel element.
50054  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50055  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50056  * @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
50057  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50058  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50059  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50060  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50061  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50062  * @cfg {String} title          The title for this panel
50063  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50064  * @cfg {String} url            Calls {@link #setUrl} with this value
50065  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50066  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50067  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50068  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50069
50070  * @constructor
50071  * Create a new ContentPanel.
50072  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50073  * @param {String/Object} config A string to set only the title or a config object
50074  * @param {String} content (optional) Set the HTML content for this panel
50075  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50076  */
50077 Roo.ContentPanel = function(el, config, content){
50078     
50079      
50080     /*
50081     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50082         config = el;
50083         el = Roo.id();
50084     }
50085     if (config && config.parentLayout) { 
50086         el = config.parentLayout.el.createChild(); 
50087     }
50088     */
50089     if(el.autoCreate){ // xtype is available if this is called from factory
50090         config = el;
50091         el = Roo.id();
50092     }
50093     this.el = Roo.get(el);
50094     if(!this.el && config && config.autoCreate){
50095         if(typeof config.autoCreate == "object"){
50096             if(!config.autoCreate.id){
50097                 config.autoCreate.id = config.id||el;
50098             }
50099             this.el = Roo.DomHelper.append(document.body,
50100                         config.autoCreate, true);
50101         }else{
50102             this.el = Roo.DomHelper.append(document.body,
50103                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50104         }
50105     }
50106     this.closable = false;
50107     this.loaded = false;
50108     this.active = false;
50109     if(typeof config == "string"){
50110         this.title = config;
50111     }else{
50112         Roo.apply(this, config);
50113     }
50114     
50115     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50116         this.wrapEl = this.el.wrap();
50117         this.toolbar.container = this.el.insertSibling(false, 'before');
50118         this.toolbar = new Roo.Toolbar(this.toolbar);
50119     }
50120     
50121     // xtype created footer. - not sure if will work as we normally have to render first..
50122     if (this.footer && !this.footer.el && this.footer.xtype) {
50123         if (!this.wrapEl) {
50124             this.wrapEl = this.el.wrap();
50125         }
50126     
50127         this.footer.container = this.wrapEl.createChild();
50128          
50129         this.footer = Roo.factory(this.footer, Roo);
50130         
50131     }
50132     
50133     if(this.resizeEl){
50134         this.resizeEl = Roo.get(this.resizeEl, true);
50135     }else{
50136         this.resizeEl = this.el;
50137     }
50138     // handle view.xtype
50139     
50140  
50141     
50142     
50143     this.addEvents({
50144         /**
50145          * @event activate
50146          * Fires when this panel is activated. 
50147          * @param {Roo.ContentPanel} this
50148          */
50149         "activate" : true,
50150         /**
50151          * @event deactivate
50152          * Fires when this panel is activated. 
50153          * @param {Roo.ContentPanel} this
50154          */
50155         "deactivate" : true,
50156
50157         /**
50158          * @event resize
50159          * Fires when this panel is resized if fitToFrame is true.
50160          * @param {Roo.ContentPanel} this
50161          * @param {Number} width The width after any component adjustments
50162          * @param {Number} height The height after any component adjustments
50163          */
50164         "resize" : true,
50165         
50166          /**
50167          * @event render
50168          * Fires when this tab is created
50169          * @param {Roo.ContentPanel} this
50170          */
50171         "render" : true
50172         
50173         
50174         
50175     });
50176     
50177
50178     
50179     
50180     if(this.autoScroll){
50181         this.resizeEl.setStyle("overflow", "auto");
50182     } else {
50183         // fix randome scrolling
50184         this.el.on('scroll', function() {
50185             Roo.log('fix random scolling');
50186             this.scrollTo('top',0); 
50187         });
50188     }
50189     content = content || this.content;
50190     if(content){
50191         this.setContent(content);
50192     }
50193     if(config && config.url){
50194         this.setUrl(this.url, this.params, this.loadOnce);
50195     }
50196     
50197     
50198     
50199     Roo.ContentPanel.superclass.constructor.call(this);
50200     
50201     if (this.view && typeof(this.view.xtype) != 'undefined') {
50202         this.view.el = this.el.appendChild(document.createElement("div"));
50203         this.view = Roo.factory(this.view); 
50204         this.view.render  &&  this.view.render(false, '');  
50205     }
50206     
50207     
50208     this.fireEvent('render', this);
50209 };
50210
50211 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50212     tabTip:'',
50213     setRegion : function(region){
50214         this.region = region;
50215         if(region){
50216            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50217         }else{
50218            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50219         } 
50220     },
50221     
50222     /**
50223      * Returns the toolbar for this Panel if one was configured. 
50224      * @return {Roo.Toolbar} 
50225      */
50226     getToolbar : function(){
50227         return this.toolbar;
50228     },
50229     
50230     setActiveState : function(active){
50231         this.active = active;
50232         if(!active){
50233             this.fireEvent("deactivate", this);
50234         }else{
50235             this.fireEvent("activate", this);
50236         }
50237     },
50238     /**
50239      * Updates this panel's element
50240      * @param {String} content The new content
50241      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50242     */
50243     setContent : function(content, loadScripts){
50244         this.el.update(content, loadScripts);
50245     },
50246
50247     ignoreResize : function(w, h){
50248         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50249             return true;
50250         }else{
50251             this.lastSize = {width: w, height: h};
50252             return false;
50253         }
50254     },
50255     /**
50256      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50257      * @return {Roo.UpdateManager} The UpdateManager
50258      */
50259     getUpdateManager : function(){
50260         return this.el.getUpdateManager();
50261     },
50262      /**
50263      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50264      * @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:
50265 <pre><code>
50266 panel.load({
50267     url: "your-url.php",
50268     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50269     callback: yourFunction,
50270     scope: yourObject, //(optional scope)
50271     discardUrl: false,
50272     nocache: false,
50273     text: "Loading...",
50274     timeout: 30,
50275     scripts: false
50276 });
50277 </code></pre>
50278      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50279      * 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.
50280      * @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}
50281      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50282      * @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.
50283      * @return {Roo.ContentPanel} this
50284      */
50285     load : function(){
50286         var um = this.el.getUpdateManager();
50287         um.update.apply(um, arguments);
50288         return this;
50289     },
50290
50291
50292     /**
50293      * 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.
50294      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50295      * @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)
50296      * @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)
50297      * @return {Roo.UpdateManager} The UpdateManager
50298      */
50299     setUrl : function(url, params, loadOnce){
50300         if(this.refreshDelegate){
50301             this.removeListener("activate", this.refreshDelegate);
50302         }
50303         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50304         this.on("activate", this.refreshDelegate);
50305         return this.el.getUpdateManager();
50306     },
50307     
50308     _handleRefresh : function(url, params, loadOnce){
50309         if(!loadOnce || !this.loaded){
50310             var updater = this.el.getUpdateManager();
50311             updater.update(url, params, this._setLoaded.createDelegate(this));
50312         }
50313     },
50314     
50315     _setLoaded : function(){
50316         this.loaded = true;
50317     }, 
50318     
50319     /**
50320      * Returns this panel's id
50321      * @return {String} 
50322      */
50323     getId : function(){
50324         return this.el.id;
50325     },
50326     
50327     /** 
50328      * Returns this panel's element - used by regiosn to add.
50329      * @return {Roo.Element} 
50330      */
50331     getEl : function(){
50332         return this.wrapEl || this.el;
50333     },
50334     
50335     adjustForComponents : function(width, height)
50336     {
50337         //Roo.log('adjustForComponents ');
50338         if(this.resizeEl != this.el){
50339             width -= this.el.getFrameWidth('lr');
50340             height -= this.el.getFrameWidth('tb');
50341         }
50342         if(this.toolbar){
50343             var te = this.toolbar.getEl();
50344             height -= te.getHeight();
50345             te.setWidth(width);
50346         }
50347         if(this.footer){
50348             var te = this.footer.getEl();
50349             Roo.log("footer:" + te.getHeight());
50350             
50351             height -= te.getHeight();
50352             te.setWidth(width);
50353         }
50354         
50355         
50356         if(this.adjustments){
50357             width += this.adjustments[0];
50358             height += this.adjustments[1];
50359         }
50360         return {"width": width, "height": height};
50361     },
50362     
50363     setSize : function(width, height){
50364         if(this.fitToFrame && !this.ignoreResize(width, height)){
50365             if(this.fitContainer && this.resizeEl != this.el){
50366                 this.el.setSize(width, height);
50367             }
50368             var size = this.adjustForComponents(width, height);
50369             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50370             this.fireEvent('resize', this, size.width, size.height);
50371         }
50372     },
50373     
50374     /**
50375      * Returns this panel's title
50376      * @return {String} 
50377      */
50378     getTitle : function(){
50379         return this.title;
50380     },
50381     
50382     /**
50383      * Set this panel's title
50384      * @param {String} title
50385      */
50386     setTitle : function(title){
50387         this.title = title;
50388         if(this.region){
50389             this.region.updatePanelTitle(this, title);
50390         }
50391     },
50392     
50393     /**
50394      * Returns true is this panel was configured to be closable
50395      * @return {Boolean} 
50396      */
50397     isClosable : function(){
50398         return this.closable;
50399     },
50400     
50401     beforeSlide : function(){
50402         this.el.clip();
50403         this.resizeEl.clip();
50404     },
50405     
50406     afterSlide : function(){
50407         this.el.unclip();
50408         this.resizeEl.unclip();
50409     },
50410     
50411     /**
50412      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50413      *   Will fail silently if the {@link #setUrl} method has not been called.
50414      *   This does not activate the panel, just updates its content.
50415      */
50416     refresh : function(){
50417         if(this.refreshDelegate){
50418            this.loaded = false;
50419            this.refreshDelegate();
50420         }
50421     },
50422     
50423     /**
50424      * Destroys this panel
50425      */
50426     destroy : function(){
50427         this.el.removeAllListeners();
50428         var tempEl = document.createElement("span");
50429         tempEl.appendChild(this.el.dom);
50430         tempEl.innerHTML = "";
50431         this.el.remove();
50432         this.el = null;
50433     },
50434     
50435     /**
50436      * form - if the content panel contains a form - this is a reference to it.
50437      * @type {Roo.form.Form}
50438      */
50439     form : false,
50440     /**
50441      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50442      *    This contains a reference to it.
50443      * @type {Roo.View}
50444      */
50445     view : false,
50446     
50447       /**
50448      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50449      * <pre><code>
50450
50451 layout.addxtype({
50452        xtype : 'Form',
50453        items: [ .... ]
50454    }
50455 );
50456
50457 </code></pre>
50458      * @param {Object} cfg Xtype definition of item to add.
50459      */
50460     
50461     addxtype : function(cfg) {
50462         // add form..
50463         if (cfg.xtype.match(/^Form$/)) {
50464             
50465             var el;
50466             //if (this.footer) {
50467             //    el = this.footer.container.insertSibling(false, 'before');
50468             //} else {
50469                 el = this.el.createChild();
50470             //}
50471
50472             this.form = new  Roo.form.Form(cfg);
50473             
50474             
50475             if ( this.form.allItems.length) this.form.render(el.dom);
50476             return this.form;
50477         }
50478         // should only have one of theses..
50479         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50480             // views.. should not be just added - used named prop 'view''
50481             
50482             cfg.el = this.el.appendChild(document.createElement("div"));
50483             // factory?
50484             
50485             var ret = new Roo.factory(cfg);
50486              
50487              ret.render && ret.render(false, ''); // render blank..
50488             this.view = ret;
50489             return ret;
50490         }
50491         return false;
50492     }
50493 });
50494
50495 /**
50496  * @class Roo.GridPanel
50497  * @extends Roo.ContentPanel
50498  * @constructor
50499  * Create a new GridPanel.
50500  * @param {Roo.grid.Grid} grid The grid for this panel
50501  * @param {String/Object} config A string to set only the panel's title, or a config object
50502  */
50503 Roo.GridPanel = function(grid, config){
50504     
50505   
50506     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50507         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50508         
50509     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50510     
50511     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50512     
50513     if(this.toolbar){
50514         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50515     }
50516     // xtype created footer. - not sure if will work as we normally have to render first..
50517     if (this.footer && !this.footer.el && this.footer.xtype) {
50518         
50519         this.footer.container = this.grid.getView().getFooterPanel(true);
50520         this.footer.dataSource = this.grid.dataSource;
50521         this.footer = Roo.factory(this.footer, Roo);
50522         
50523     }
50524     
50525     grid.monitorWindowResize = false; // turn off autosizing
50526     grid.autoHeight = false;
50527     grid.autoWidth = false;
50528     this.grid = grid;
50529     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50530 };
50531
50532 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50533     getId : function(){
50534         return this.grid.id;
50535     },
50536     
50537     /**
50538      * Returns the grid for this panel
50539      * @return {Roo.grid.Grid} 
50540      */
50541     getGrid : function(){
50542         return this.grid;    
50543     },
50544     
50545     setSize : function(width, height){
50546         if(!this.ignoreResize(width, height)){
50547             var grid = this.grid;
50548             var size = this.adjustForComponents(width, height);
50549             grid.getGridEl().setSize(size.width, size.height);
50550             grid.autoSize();
50551         }
50552     },
50553     
50554     beforeSlide : function(){
50555         this.grid.getView().scroller.clip();
50556     },
50557     
50558     afterSlide : function(){
50559         this.grid.getView().scroller.unclip();
50560     },
50561     
50562     destroy : function(){
50563         this.grid.destroy();
50564         delete this.grid;
50565         Roo.GridPanel.superclass.destroy.call(this); 
50566     }
50567 });
50568
50569
50570 /**
50571  * @class Roo.NestedLayoutPanel
50572  * @extends Roo.ContentPanel
50573  * @constructor
50574  * Create a new NestedLayoutPanel.
50575  * 
50576  * 
50577  * @param {Roo.BorderLayout} layout The layout for this panel
50578  * @param {String/Object} config A string to set only the title or a config object
50579  */
50580 Roo.NestedLayoutPanel = function(layout, config)
50581 {
50582     // construct with only one argument..
50583     /* FIXME - implement nicer consturctors
50584     if (layout.layout) {
50585         config = layout;
50586         layout = config.layout;
50587         delete config.layout;
50588     }
50589     if (layout.xtype && !layout.getEl) {
50590         // then layout needs constructing..
50591         layout = Roo.factory(layout, Roo);
50592     }
50593     */
50594     
50595     
50596     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50597     
50598     layout.monitorWindowResize = false; // turn off autosizing
50599     this.layout = layout;
50600     this.layout.getEl().addClass("x-layout-nested-layout");
50601     
50602     
50603     
50604     
50605 };
50606
50607 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50608
50609     setSize : function(width, height){
50610         if(!this.ignoreResize(width, height)){
50611             var size = this.adjustForComponents(width, height);
50612             var el = this.layout.getEl();
50613             el.setSize(size.width, size.height);
50614             var touch = el.dom.offsetWidth;
50615             this.layout.layout();
50616             // ie requires a double layout on the first pass
50617             if(Roo.isIE && !this.initialized){
50618                 this.initialized = true;
50619                 this.layout.layout();
50620             }
50621         }
50622     },
50623     
50624     // activate all subpanels if not currently active..
50625     
50626     setActiveState : function(active){
50627         this.active = active;
50628         if(!active){
50629             this.fireEvent("deactivate", this);
50630             return;
50631         }
50632         
50633         this.fireEvent("activate", this);
50634         // not sure if this should happen before or after..
50635         if (!this.layout) {
50636             return; // should not happen..
50637         }
50638         var reg = false;
50639         for (var r in this.layout.regions) {
50640             reg = this.layout.getRegion(r);
50641             if (reg.getActivePanel()) {
50642                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50643                 reg.setActivePanel(reg.getActivePanel());
50644                 continue;
50645             }
50646             if (!reg.panels.length) {
50647                 continue;
50648             }
50649             reg.showPanel(reg.getPanel(0));
50650         }
50651         
50652         
50653         
50654         
50655     },
50656     
50657     /**
50658      * Returns the nested BorderLayout for this panel
50659      * @return {Roo.BorderLayout} 
50660      */
50661     getLayout : function(){
50662         return this.layout;
50663     },
50664     
50665      /**
50666      * Adds a xtype elements to the layout of the nested panel
50667      * <pre><code>
50668
50669 panel.addxtype({
50670        xtype : 'ContentPanel',
50671        region: 'west',
50672        items: [ .... ]
50673    }
50674 );
50675
50676 panel.addxtype({
50677         xtype : 'NestedLayoutPanel',
50678         region: 'west',
50679         layout: {
50680            center: { },
50681            west: { }   
50682         },
50683         items : [ ... list of content panels or nested layout panels.. ]
50684    }
50685 );
50686 </code></pre>
50687      * @param {Object} cfg Xtype definition of item to add.
50688      */
50689     addxtype : function(cfg) {
50690         return this.layout.addxtype(cfg);
50691     
50692     }
50693 });
50694
50695 Roo.ScrollPanel = function(el, config, content){
50696     config = config || {};
50697     config.fitToFrame = true;
50698     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50699     
50700     this.el.dom.style.overflow = "hidden";
50701     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50702     this.el.removeClass("x-layout-inactive-content");
50703     this.el.on("mousewheel", this.onWheel, this);
50704
50705     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50706     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50707     up.unselectable(); down.unselectable();
50708     up.on("click", this.scrollUp, this);
50709     down.on("click", this.scrollDown, this);
50710     up.addClassOnOver("x-scroller-btn-over");
50711     down.addClassOnOver("x-scroller-btn-over");
50712     up.addClassOnClick("x-scroller-btn-click");
50713     down.addClassOnClick("x-scroller-btn-click");
50714     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50715
50716     this.resizeEl = this.el;
50717     this.el = wrap; this.up = up; this.down = down;
50718 };
50719
50720 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50721     increment : 100,
50722     wheelIncrement : 5,
50723     scrollUp : function(){
50724         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50725     },
50726
50727     scrollDown : function(){
50728         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50729     },
50730
50731     afterScroll : function(){
50732         var el = this.resizeEl;
50733         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50734         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50735         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50736     },
50737
50738     setSize : function(){
50739         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50740         this.afterScroll();
50741     },
50742
50743     onWheel : function(e){
50744         var d = e.getWheelDelta();
50745         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50746         this.afterScroll();
50747         e.stopEvent();
50748     },
50749
50750     setContent : function(content, loadScripts){
50751         this.resizeEl.update(content, loadScripts);
50752     }
50753
50754 });
50755
50756
50757
50758
50759
50760
50761
50762
50763
50764 /**
50765  * @class Roo.TreePanel
50766  * @extends Roo.ContentPanel
50767  * @constructor
50768  * Create a new TreePanel. - defaults to fit/scoll contents.
50769  * @param {String/Object} config A string to set only the panel's title, or a config object
50770  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50771  */
50772 Roo.TreePanel = function(config){
50773     var el = config.el;
50774     var tree = config.tree;
50775     delete config.tree; 
50776     delete config.el; // hopefull!
50777     
50778     // wrapper for IE7 strict & safari scroll issue
50779     
50780     var treeEl = el.createChild();
50781     config.resizeEl = treeEl;
50782     
50783     
50784     
50785     Roo.TreePanel.superclass.constructor.call(this, el, config);
50786  
50787  
50788     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50789     //console.log(tree);
50790     this.on('activate', function()
50791     {
50792         if (this.tree.rendered) {
50793             return;
50794         }
50795         //console.log('render tree');
50796         this.tree.render();
50797     });
50798     // this should not be needed.. - it's actually the 'el' that resizes?
50799     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50800     
50801     //this.on('resize',  function (cp, w, h) {
50802     //        this.tree.innerCt.setWidth(w);
50803     //        this.tree.innerCt.setHeight(h);
50804     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50805     //});
50806
50807         
50808     
50809 };
50810
50811 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50812     fitToFrame : true,
50813     autoScroll : true
50814 });
50815
50816
50817
50818
50819
50820
50821
50822
50823
50824
50825
50826 /*
50827  * Based on:
50828  * Ext JS Library 1.1.1
50829  * Copyright(c) 2006-2007, Ext JS, LLC.
50830  *
50831  * Originally Released Under LGPL - original licence link has changed is not relivant.
50832  *
50833  * Fork - LGPL
50834  * <script type="text/javascript">
50835  */
50836  
50837
50838 /**
50839  * @class Roo.ReaderLayout
50840  * @extends Roo.BorderLayout
50841  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50842  * center region containing two nested regions (a top one for a list view and one for item preview below),
50843  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50844  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50845  * expedites the setup of the overall layout and regions for this common application style.
50846  * Example:
50847  <pre><code>
50848 var reader = new Roo.ReaderLayout();
50849 var CP = Roo.ContentPanel;  // shortcut for adding
50850
50851 reader.beginUpdate();
50852 reader.add("north", new CP("north", "North"));
50853 reader.add("west", new CP("west", {title: "West"}));
50854 reader.add("east", new CP("east", {title: "East"}));
50855
50856 reader.regions.listView.add(new CP("listView", "List"));
50857 reader.regions.preview.add(new CP("preview", "Preview"));
50858 reader.endUpdate();
50859 </code></pre>
50860 * @constructor
50861 * Create a new ReaderLayout
50862 * @param {Object} config Configuration options
50863 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50864 * document.body if omitted)
50865 */
50866 Roo.ReaderLayout = function(config, renderTo){
50867     var c = config || {size:{}};
50868     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50869         north: c.north !== false ? Roo.apply({
50870             split:false,
50871             initialSize: 32,
50872             titlebar: false
50873         }, c.north) : false,
50874         west: c.west !== false ? Roo.apply({
50875             split:true,
50876             initialSize: 200,
50877             minSize: 175,
50878             maxSize: 400,
50879             titlebar: true,
50880             collapsible: true,
50881             animate: true,
50882             margins:{left:5,right:0,bottom:5,top:5},
50883             cmargins:{left:5,right:5,bottom:5,top:5}
50884         }, c.west) : false,
50885         east: c.east !== false ? Roo.apply({
50886             split:true,
50887             initialSize: 200,
50888             minSize: 175,
50889             maxSize: 400,
50890             titlebar: true,
50891             collapsible: true,
50892             animate: true,
50893             margins:{left:0,right:5,bottom:5,top:5},
50894             cmargins:{left:5,right:5,bottom:5,top:5}
50895         }, c.east) : false,
50896         center: Roo.apply({
50897             tabPosition: 'top',
50898             autoScroll:false,
50899             closeOnTab: true,
50900             titlebar:false,
50901             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50902         }, c.center)
50903     });
50904
50905     this.el.addClass('x-reader');
50906
50907     this.beginUpdate();
50908
50909     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50910         south: c.preview !== false ? Roo.apply({
50911             split:true,
50912             initialSize: 200,
50913             minSize: 100,
50914             autoScroll:true,
50915             collapsible:true,
50916             titlebar: true,
50917             cmargins:{top:5,left:0, right:0, bottom:0}
50918         }, c.preview) : false,
50919         center: Roo.apply({
50920             autoScroll:false,
50921             titlebar:false,
50922             minHeight:200
50923         }, c.listView)
50924     });
50925     this.add('center', new Roo.NestedLayoutPanel(inner,
50926             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50927
50928     this.endUpdate();
50929
50930     this.regions.preview = inner.getRegion('south');
50931     this.regions.listView = inner.getRegion('center');
50932 };
50933
50934 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50935  * Based on:
50936  * Ext JS Library 1.1.1
50937  * Copyright(c) 2006-2007, Ext JS, LLC.
50938  *
50939  * Originally Released Under LGPL - original licence link has changed is not relivant.
50940  *
50941  * Fork - LGPL
50942  * <script type="text/javascript">
50943  */
50944  
50945 /**
50946  * @class Roo.grid.Grid
50947  * @extends Roo.util.Observable
50948  * This class represents the primary interface of a component based grid control.
50949  * <br><br>Usage:<pre><code>
50950  var grid = new Roo.grid.Grid("my-container-id", {
50951      ds: myDataStore,
50952      cm: myColModel,
50953      selModel: mySelectionModel,
50954      autoSizeColumns: true,
50955      monitorWindowResize: false,
50956      trackMouseOver: true
50957  });
50958  // set any options
50959  grid.render();
50960  * </code></pre>
50961  * <b>Common Problems:</b><br/>
50962  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
50963  * element will correct this<br/>
50964  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
50965  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
50966  * are unpredictable.<br/>
50967  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
50968  * grid to calculate dimensions/offsets.<br/>
50969   * @constructor
50970  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50971  * The container MUST have some type of size defined for the grid to fill. The container will be
50972  * automatically set to position relative if it isn't already.
50973  * @param {Object} config A config object that sets properties on this grid.
50974  */
50975 Roo.grid.Grid = function(container, config){
50976         // initialize the container
50977         this.container = Roo.get(container);
50978         this.container.update("");
50979         this.container.setStyle("overflow", "hidden");
50980     this.container.addClass('x-grid-container');
50981
50982     this.id = this.container.id;
50983
50984     Roo.apply(this, config);
50985     // check and correct shorthanded configs
50986     if(this.ds){
50987         this.dataSource = this.ds;
50988         delete this.ds;
50989     }
50990     if(this.cm){
50991         this.colModel = this.cm;
50992         delete this.cm;
50993     }
50994     if(this.sm){
50995         this.selModel = this.sm;
50996         delete this.sm;
50997     }
50998
50999     if (this.selModel) {
51000         this.selModel = Roo.factory(this.selModel, Roo.grid);
51001         this.sm = this.selModel;
51002         this.sm.xmodule = this.xmodule || false;
51003     }
51004     if (typeof(this.colModel.config) == 'undefined') {
51005         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51006         this.cm = this.colModel;
51007         this.cm.xmodule = this.xmodule || false;
51008     }
51009     if (this.dataSource) {
51010         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51011         this.ds = this.dataSource;
51012         this.ds.xmodule = this.xmodule || false;
51013          
51014     }
51015     
51016     
51017     
51018     if(this.width){
51019         this.container.setWidth(this.width);
51020     }
51021
51022     if(this.height){
51023         this.container.setHeight(this.height);
51024     }
51025     /** @private */
51026         this.addEvents({
51027         // raw events
51028         /**
51029          * @event click
51030          * The raw click event for the entire grid.
51031          * @param {Roo.EventObject} e
51032          */
51033         "click" : true,
51034         /**
51035          * @event dblclick
51036          * The raw dblclick event for the entire grid.
51037          * @param {Roo.EventObject} e
51038          */
51039         "dblclick" : true,
51040         /**
51041          * @event contextmenu
51042          * The raw contextmenu event for the entire grid.
51043          * @param {Roo.EventObject} e
51044          */
51045         "contextmenu" : true,
51046         /**
51047          * @event mousedown
51048          * The raw mousedown event for the entire grid.
51049          * @param {Roo.EventObject} e
51050          */
51051         "mousedown" : true,
51052         /**
51053          * @event mouseup
51054          * The raw mouseup event for the entire grid.
51055          * @param {Roo.EventObject} e
51056          */
51057         "mouseup" : true,
51058         /**
51059          * @event mouseover
51060          * The raw mouseover event for the entire grid.
51061          * @param {Roo.EventObject} e
51062          */
51063         "mouseover" : true,
51064         /**
51065          * @event mouseout
51066          * The raw mouseout event for the entire grid.
51067          * @param {Roo.EventObject} e
51068          */
51069         "mouseout" : true,
51070         /**
51071          * @event keypress
51072          * The raw keypress event for the entire grid.
51073          * @param {Roo.EventObject} e
51074          */
51075         "keypress" : true,
51076         /**
51077          * @event keydown
51078          * The raw keydown event for the entire grid.
51079          * @param {Roo.EventObject} e
51080          */
51081         "keydown" : true,
51082
51083         // custom events
51084
51085         /**
51086          * @event cellclick
51087          * Fires when a cell is clicked
51088          * @param {Grid} this
51089          * @param {Number} rowIndex
51090          * @param {Number} columnIndex
51091          * @param {Roo.EventObject} e
51092          */
51093         "cellclick" : true,
51094         /**
51095          * @event celldblclick
51096          * Fires when a cell is double clicked
51097          * @param {Grid} this
51098          * @param {Number} rowIndex
51099          * @param {Number} columnIndex
51100          * @param {Roo.EventObject} e
51101          */
51102         "celldblclick" : true,
51103         /**
51104          * @event rowclick
51105          * Fires when a row is clicked
51106          * @param {Grid} this
51107          * @param {Number} rowIndex
51108          * @param {Roo.EventObject} e
51109          */
51110         "rowclick" : true,
51111         /**
51112          * @event rowdblclick
51113          * Fires when a row is double clicked
51114          * @param {Grid} this
51115          * @param {Number} rowIndex
51116          * @param {Roo.EventObject} e
51117          */
51118         "rowdblclick" : true,
51119         /**
51120          * @event headerclick
51121          * Fires when a header is clicked
51122          * @param {Grid} this
51123          * @param {Number} columnIndex
51124          * @param {Roo.EventObject} e
51125          */
51126         "headerclick" : true,
51127         /**
51128          * @event headerdblclick
51129          * Fires when a header cell is double clicked
51130          * @param {Grid} this
51131          * @param {Number} columnIndex
51132          * @param {Roo.EventObject} e
51133          */
51134         "headerdblclick" : true,
51135         /**
51136          * @event rowcontextmenu
51137          * Fires when a row is right clicked
51138          * @param {Grid} this
51139          * @param {Number} rowIndex
51140          * @param {Roo.EventObject} e
51141          */
51142         "rowcontextmenu" : true,
51143         /**
51144          * @event cellcontextmenu
51145          * Fires when a cell is right clicked
51146          * @param {Grid} this
51147          * @param {Number} rowIndex
51148          * @param {Number} cellIndex
51149          * @param {Roo.EventObject} e
51150          */
51151          "cellcontextmenu" : true,
51152         /**
51153          * @event headercontextmenu
51154          * Fires when a header is right clicked
51155          * @param {Grid} this
51156          * @param {Number} columnIndex
51157          * @param {Roo.EventObject} e
51158          */
51159         "headercontextmenu" : true,
51160         /**
51161          * @event bodyscroll
51162          * Fires when the body element is scrolled
51163          * @param {Number} scrollLeft
51164          * @param {Number} scrollTop
51165          */
51166         "bodyscroll" : true,
51167         /**
51168          * @event columnresize
51169          * Fires when the user resizes a column
51170          * @param {Number} columnIndex
51171          * @param {Number} newSize
51172          */
51173         "columnresize" : true,
51174         /**
51175          * @event columnmove
51176          * Fires when the user moves a column
51177          * @param {Number} oldIndex
51178          * @param {Number} newIndex
51179          */
51180         "columnmove" : true,
51181         /**
51182          * @event startdrag
51183          * Fires when row(s) start being dragged
51184          * @param {Grid} this
51185          * @param {Roo.GridDD} dd The drag drop object
51186          * @param {event} e The raw browser event
51187          */
51188         "startdrag" : true,
51189         /**
51190          * @event enddrag
51191          * Fires when a drag operation is complete
51192          * @param {Grid} this
51193          * @param {Roo.GridDD} dd The drag drop object
51194          * @param {event} e The raw browser event
51195          */
51196         "enddrag" : true,
51197         /**
51198          * @event dragdrop
51199          * Fires when dragged row(s) are dropped on a valid DD target
51200          * @param {Grid} this
51201          * @param {Roo.GridDD} dd The drag drop object
51202          * @param {String} targetId The target drag drop object
51203          * @param {event} e The raw browser event
51204          */
51205         "dragdrop" : true,
51206         /**
51207          * @event dragover
51208          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51209          * @param {Grid} this
51210          * @param {Roo.GridDD} dd The drag drop object
51211          * @param {String} targetId The target drag drop object
51212          * @param {event} e The raw browser event
51213          */
51214         "dragover" : true,
51215         /**
51216          * @event dragenter
51217          *  Fires when the dragged row(s) first cross another DD target while being dragged
51218          * @param {Grid} this
51219          * @param {Roo.GridDD} dd The drag drop object
51220          * @param {String} targetId The target drag drop object
51221          * @param {event} e The raw browser event
51222          */
51223         "dragenter" : true,
51224         /**
51225          * @event dragout
51226          * Fires when the dragged row(s) leave another DD target while being dragged
51227          * @param {Grid} this
51228          * @param {Roo.GridDD} dd The drag drop object
51229          * @param {String} targetId The target drag drop object
51230          * @param {event} e The raw browser event
51231          */
51232         "dragout" : true,
51233         /**
51234          * @event rowclass
51235          * Fires when a row is rendered, so you can change add a style to it.
51236          * @param {GridView} gridview   The grid view
51237          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51238          */
51239         'rowclass' : true,
51240
51241         /**
51242          * @event render
51243          * Fires when the grid is rendered
51244          * @param {Grid} grid
51245          */
51246         'render' : true
51247     });
51248
51249     Roo.grid.Grid.superclass.constructor.call(this);
51250 };
51251 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51252     
51253     /**
51254      * @cfg {String} ddGroup - drag drop group.
51255      */
51256
51257     /**
51258      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51259      */
51260     minColumnWidth : 25,
51261
51262     /**
51263      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51264      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51265      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51266      */
51267     autoSizeColumns : false,
51268
51269     /**
51270      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51271      */
51272     autoSizeHeaders : true,
51273
51274     /**
51275      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51276      */
51277     monitorWindowResize : true,
51278
51279     /**
51280      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51281      * rows measured to get a columns size. Default is 0 (all rows).
51282      */
51283     maxRowsToMeasure : 0,
51284
51285     /**
51286      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51287      */
51288     trackMouseOver : true,
51289
51290     /**
51291     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51292     */
51293     
51294     /**
51295     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51296     */
51297     enableDragDrop : false,
51298     
51299     /**
51300     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51301     */
51302     enableColumnMove : true,
51303     
51304     /**
51305     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51306     */
51307     enableColumnHide : true,
51308     
51309     /**
51310     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51311     */
51312     enableRowHeightSync : false,
51313     
51314     /**
51315     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51316     */
51317     stripeRows : true,
51318     
51319     /**
51320     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51321     */
51322     autoHeight : false,
51323
51324     /**
51325      * @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.
51326      */
51327     autoExpandColumn : false,
51328
51329     /**
51330     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51331     * Default is 50.
51332     */
51333     autoExpandMin : 50,
51334
51335     /**
51336     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51337     */
51338     autoExpandMax : 1000,
51339
51340     /**
51341     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51342     */
51343     view : null,
51344
51345     /**
51346     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51347     */
51348     loadMask : false,
51349     /**
51350     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51351     */
51352     dropTarget: false,
51353     
51354    
51355     
51356     // private
51357     rendered : false,
51358
51359     /**
51360     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51361     * of a fixed width. Default is false.
51362     */
51363     /**
51364     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51365     */
51366     /**
51367      * Called once after all setup has been completed and the grid is ready to be rendered.
51368      * @return {Roo.grid.Grid} this
51369      */
51370     render : function()
51371     {
51372         var c = this.container;
51373         // try to detect autoHeight/width mode
51374         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51375             this.autoHeight = true;
51376         }
51377         var view = this.getView();
51378         view.init(this);
51379
51380         c.on("click", this.onClick, this);
51381         c.on("dblclick", this.onDblClick, this);
51382         c.on("contextmenu", this.onContextMenu, this);
51383         c.on("keydown", this.onKeyDown, this);
51384         if (Roo.isTouch) {
51385             c.on("touchstart", this.onTouchStart, this);
51386         }
51387
51388         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51389
51390         this.getSelectionModel().init(this);
51391
51392         view.render();
51393
51394         if(this.loadMask){
51395             this.loadMask = new Roo.LoadMask(this.container,
51396                     Roo.apply({store:this.dataSource}, this.loadMask));
51397         }
51398         
51399         
51400         if (this.toolbar && this.toolbar.xtype) {
51401             this.toolbar.container = this.getView().getHeaderPanel(true);
51402             this.toolbar = new Roo.Toolbar(this.toolbar);
51403         }
51404         if (this.footer && this.footer.xtype) {
51405             this.footer.dataSource = this.getDataSource();
51406             this.footer.container = this.getView().getFooterPanel(true);
51407             this.footer = Roo.factory(this.footer, Roo);
51408         }
51409         if (this.dropTarget && this.dropTarget.xtype) {
51410             delete this.dropTarget.xtype;
51411             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51412         }
51413         
51414         
51415         this.rendered = true;
51416         this.fireEvent('render', this);
51417         return this;
51418     },
51419
51420         /**
51421          * Reconfigures the grid to use a different Store and Column Model.
51422          * The View will be bound to the new objects and refreshed.
51423          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51424          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51425          */
51426     reconfigure : function(dataSource, colModel){
51427         if(this.loadMask){
51428             this.loadMask.destroy();
51429             this.loadMask = new Roo.LoadMask(this.container,
51430                     Roo.apply({store:dataSource}, this.loadMask));
51431         }
51432         this.view.bind(dataSource, colModel);
51433         this.dataSource = dataSource;
51434         this.colModel = colModel;
51435         this.view.refresh(true);
51436     },
51437
51438     // private
51439     onKeyDown : function(e){
51440         this.fireEvent("keydown", e);
51441     },
51442
51443     /**
51444      * Destroy this grid.
51445      * @param {Boolean} removeEl True to remove the element
51446      */
51447     destroy : function(removeEl, keepListeners){
51448         if(this.loadMask){
51449             this.loadMask.destroy();
51450         }
51451         var c = this.container;
51452         c.removeAllListeners();
51453         this.view.destroy();
51454         this.colModel.purgeListeners();
51455         if(!keepListeners){
51456             this.purgeListeners();
51457         }
51458         c.update("");
51459         if(removeEl === true){
51460             c.remove();
51461         }
51462     },
51463
51464     // private
51465     processEvent : function(name, e){
51466         // does this fire select???
51467         Roo.log('grid:processEvent '  + name);
51468         
51469         if (name != 'touchstart' ) {
51470             this.fireEvent(name, e);    
51471         }
51472         
51473         var t = e.getTarget();
51474         var v = this.view;
51475         var header = v.findHeaderIndex(t);
51476         if(header !== false){
51477             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51478         }else{
51479             var row = v.findRowIndex(t);
51480             var cell = v.findCellIndex(t);
51481             if (name == 'touchstart') {
51482                 // first touch is always a click.
51483                 // hopefull this happens after selection is updated.?
51484                 name = false;
51485                 
51486                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51487                     var cs = this.selModel.getSelectedCell();
51488                     if (row == cs[0] && cell == cs[1]){
51489                         name = 'dblclick';
51490                     }
51491                 }
51492                 if (typeof(this.selModel.getSelections) != 'undefined') {
51493                     var cs = this.selModel.getSelections();
51494                     var ds = this.dataSource;
51495                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51496                         name = 'dblclick';
51497                     }
51498                 }
51499                 if (!name) {
51500                     return;
51501                 }
51502             }
51503             
51504             
51505             if(row !== false){
51506                 this.fireEvent("row" + name, this, row, e);
51507                 if(cell !== false){
51508                     this.fireEvent("cell" + name, this, row, cell, e);
51509                 }
51510             }
51511         }
51512     },
51513
51514     // private
51515     onClick : function(e){
51516         this.processEvent("click", e);
51517     },
51518    // private
51519     onTouchStart : function(e){
51520         this.processEvent("touchstart", e);
51521     },
51522
51523     // private
51524     onContextMenu : function(e, t){
51525         this.processEvent("contextmenu", e);
51526     },
51527
51528     // private
51529     onDblClick : function(e){
51530         this.processEvent("dblclick", e);
51531     },
51532
51533     // private
51534     walkCells : function(row, col, step, fn, scope){
51535         var cm = this.colModel, clen = cm.getColumnCount();
51536         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51537         if(step < 0){
51538             if(col < 0){
51539                 row--;
51540                 first = false;
51541             }
51542             while(row >= 0){
51543                 if(!first){
51544                     col = clen-1;
51545                 }
51546                 first = false;
51547                 while(col >= 0){
51548                     if(fn.call(scope || this, row, col, cm) === true){
51549                         return [row, col];
51550                     }
51551                     col--;
51552                 }
51553                 row--;
51554             }
51555         } else {
51556             if(col >= clen){
51557                 row++;
51558                 first = false;
51559             }
51560             while(row < rlen){
51561                 if(!first){
51562                     col = 0;
51563                 }
51564                 first = false;
51565                 while(col < clen){
51566                     if(fn.call(scope || this, row, col, cm) === true){
51567                         return [row, col];
51568                     }
51569                     col++;
51570                 }
51571                 row++;
51572             }
51573         }
51574         return null;
51575     },
51576
51577     // private
51578     getSelections : function(){
51579         return this.selModel.getSelections();
51580     },
51581
51582     /**
51583      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51584      * but if manual update is required this method will initiate it.
51585      */
51586     autoSize : function(){
51587         if(this.rendered){
51588             this.view.layout();
51589             if(this.view.adjustForScroll){
51590                 this.view.adjustForScroll();
51591             }
51592         }
51593     },
51594
51595     /**
51596      * Returns the grid's underlying element.
51597      * @return {Element} The element
51598      */
51599     getGridEl : function(){
51600         return this.container;
51601     },
51602
51603     // private for compatibility, overridden by editor grid
51604     stopEditing : function(){},
51605
51606     /**
51607      * Returns the grid's SelectionModel.
51608      * @return {SelectionModel}
51609      */
51610     getSelectionModel : function(){
51611         if(!this.selModel){
51612             this.selModel = new Roo.grid.RowSelectionModel();
51613         }
51614         return this.selModel;
51615     },
51616
51617     /**
51618      * Returns the grid's DataSource.
51619      * @return {DataSource}
51620      */
51621     getDataSource : function(){
51622         return this.dataSource;
51623     },
51624
51625     /**
51626      * Returns the grid's ColumnModel.
51627      * @return {ColumnModel}
51628      */
51629     getColumnModel : function(){
51630         return this.colModel;
51631     },
51632
51633     /**
51634      * Returns the grid's GridView object.
51635      * @return {GridView}
51636      */
51637     getView : function(){
51638         if(!this.view){
51639             this.view = new Roo.grid.GridView(this.viewConfig);
51640         }
51641         return this.view;
51642     },
51643     /**
51644      * Called to get grid's drag proxy text, by default returns this.ddText.
51645      * @return {String}
51646      */
51647     getDragDropText : function(){
51648         var count = this.selModel.getCount();
51649         return String.format(this.ddText, count, count == 1 ? '' : 's');
51650     }
51651 });
51652 /**
51653  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51654  * %0 is replaced with the number of selected rows.
51655  * @type String
51656  */
51657 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51658  * Based on:
51659  * Ext JS Library 1.1.1
51660  * Copyright(c) 2006-2007, Ext JS, LLC.
51661  *
51662  * Originally Released Under LGPL - original licence link has changed is not relivant.
51663  *
51664  * Fork - LGPL
51665  * <script type="text/javascript">
51666  */
51667  
51668 Roo.grid.AbstractGridView = function(){
51669         this.grid = null;
51670         
51671         this.events = {
51672             "beforerowremoved" : true,
51673             "beforerowsinserted" : true,
51674             "beforerefresh" : true,
51675             "rowremoved" : true,
51676             "rowsinserted" : true,
51677             "rowupdated" : true,
51678             "refresh" : true
51679         };
51680     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51681 };
51682
51683 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51684     rowClass : "x-grid-row",
51685     cellClass : "x-grid-cell",
51686     tdClass : "x-grid-td",
51687     hdClass : "x-grid-hd",
51688     splitClass : "x-grid-hd-split",
51689     
51690         init: function(grid){
51691         this.grid = grid;
51692                 var cid = this.grid.getGridEl().id;
51693         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51694         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51695         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51696         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51697         },
51698         
51699         getColumnRenderers : function(){
51700         var renderers = [];
51701         var cm = this.grid.colModel;
51702         var colCount = cm.getColumnCount();
51703         for(var i = 0; i < colCount; i++){
51704             renderers[i] = cm.getRenderer(i);
51705         }
51706         return renderers;
51707     },
51708     
51709     getColumnIds : function(){
51710         var ids = [];
51711         var cm = this.grid.colModel;
51712         var colCount = cm.getColumnCount();
51713         for(var i = 0; i < colCount; i++){
51714             ids[i] = cm.getColumnId(i);
51715         }
51716         return ids;
51717     },
51718     
51719     getDataIndexes : function(){
51720         if(!this.indexMap){
51721             this.indexMap = this.buildIndexMap();
51722         }
51723         return this.indexMap.colToData;
51724     },
51725     
51726     getColumnIndexByDataIndex : function(dataIndex){
51727         if(!this.indexMap){
51728             this.indexMap = this.buildIndexMap();
51729         }
51730         return this.indexMap.dataToCol[dataIndex];
51731     },
51732     
51733     /**
51734      * Set a css style for a column dynamically. 
51735      * @param {Number} colIndex The index of the column
51736      * @param {String} name The css property name
51737      * @param {String} value The css value
51738      */
51739     setCSSStyle : function(colIndex, name, value){
51740         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51741         Roo.util.CSS.updateRule(selector, name, value);
51742     },
51743     
51744     generateRules : function(cm){
51745         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51746         Roo.util.CSS.removeStyleSheet(rulesId);
51747         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51748             var cid = cm.getColumnId(i);
51749             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51750                          this.tdSelector, cid, " {\n}\n",
51751                          this.hdSelector, cid, " {\n}\n",
51752                          this.splitSelector, cid, " {\n}\n");
51753         }
51754         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51755     }
51756 });/*
51757  * Based on:
51758  * Ext JS Library 1.1.1
51759  * Copyright(c) 2006-2007, Ext JS, LLC.
51760  *
51761  * Originally Released Under LGPL - original licence link has changed is not relivant.
51762  *
51763  * Fork - LGPL
51764  * <script type="text/javascript">
51765  */
51766
51767 // private
51768 // This is a support class used internally by the Grid components
51769 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51770     this.grid = grid;
51771     this.view = grid.getView();
51772     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51773     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51774     if(hd2){
51775         this.setHandleElId(Roo.id(hd));
51776         this.setOuterHandleElId(Roo.id(hd2));
51777     }
51778     this.scroll = false;
51779 };
51780 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51781     maxDragWidth: 120,
51782     getDragData : function(e){
51783         var t = Roo.lib.Event.getTarget(e);
51784         var h = this.view.findHeaderCell(t);
51785         if(h){
51786             return {ddel: h.firstChild, header:h};
51787         }
51788         return false;
51789     },
51790
51791     onInitDrag : function(e){
51792         this.view.headersDisabled = true;
51793         var clone = this.dragData.ddel.cloneNode(true);
51794         clone.id = Roo.id();
51795         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51796         this.proxy.update(clone);
51797         return true;
51798     },
51799
51800     afterValidDrop : function(){
51801         var v = this.view;
51802         setTimeout(function(){
51803             v.headersDisabled = false;
51804         }, 50);
51805     },
51806
51807     afterInvalidDrop : function(){
51808         var v = this.view;
51809         setTimeout(function(){
51810             v.headersDisabled = false;
51811         }, 50);
51812     }
51813 });
51814 /*
51815  * Based on:
51816  * Ext JS Library 1.1.1
51817  * Copyright(c) 2006-2007, Ext JS, LLC.
51818  *
51819  * Originally Released Under LGPL - original licence link has changed is not relivant.
51820  *
51821  * Fork - LGPL
51822  * <script type="text/javascript">
51823  */
51824 // private
51825 // This is a support class used internally by the Grid components
51826 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51827     this.grid = grid;
51828     this.view = grid.getView();
51829     // split the proxies so they don't interfere with mouse events
51830     this.proxyTop = Roo.DomHelper.append(document.body, {
51831         cls:"col-move-top", html:"&#160;"
51832     }, true);
51833     this.proxyBottom = Roo.DomHelper.append(document.body, {
51834         cls:"col-move-bottom", html:"&#160;"
51835     }, true);
51836     this.proxyTop.hide = this.proxyBottom.hide = function(){
51837         this.setLeftTop(-100,-100);
51838         this.setStyle("visibility", "hidden");
51839     };
51840     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51841     // temporarily disabled
51842     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51843     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51844 };
51845 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51846     proxyOffsets : [-4, -9],
51847     fly: Roo.Element.fly,
51848
51849     getTargetFromEvent : function(e){
51850         var t = Roo.lib.Event.getTarget(e);
51851         var cindex = this.view.findCellIndex(t);
51852         if(cindex !== false){
51853             return this.view.getHeaderCell(cindex);
51854         }
51855         return null;
51856     },
51857
51858     nextVisible : function(h){
51859         var v = this.view, cm = this.grid.colModel;
51860         h = h.nextSibling;
51861         while(h){
51862             if(!cm.isHidden(v.getCellIndex(h))){
51863                 return h;
51864             }
51865             h = h.nextSibling;
51866         }
51867         return null;
51868     },
51869
51870     prevVisible : function(h){
51871         var v = this.view, cm = this.grid.colModel;
51872         h = h.prevSibling;
51873         while(h){
51874             if(!cm.isHidden(v.getCellIndex(h))){
51875                 return h;
51876             }
51877             h = h.prevSibling;
51878         }
51879         return null;
51880     },
51881
51882     positionIndicator : function(h, n, e){
51883         var x = Roo.lib.Event.getPageX(e);
51884         var r = Roo.lib.Dom.getRegion(n.firstChild);
51885         var px, pt, py = r.top + this.proxyOffsets[1];
51886         if((r.right - x) <= (r.right-r.left)/2){
51887             px = r.right+this.view.borderWidth;
51888             pt = "after";
51889         }else{
51890             px = r.left;
51891             pt = "before";
51892         }
51893         var oldIndex = this.view.getCellIndex(h);
51894         var newIndex = this.view.getCellIndex(n);
51895
51896         if(this.grid.colModel.isFixed(newIndex)){
51897             return false;
51898         }
51899
51900         var locked = this.grid.colModel.isLocked(newIndex);
51901
51902         if(pt == "after"){
51903             newIndex++;
51904         }
51905         if(oldIndex < newIndex){
51906             newIndex--;
51907         }
51908         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51909             return false;
51910         }
51911         px +=  this.proxyOffsets[0];
51912         this.proxyTop.setLeftTop(px, py);
51913         this.proxyTop.show();
51914         if(!this.bottomOffset){
51915             this.bottomOffset = this.view.mainHd.getHeight();
51916         }
51917         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51918         this.proxyBottom.show();
51919         return pt;
51920     },
51921
51922     onNodeEnter : function(n, dd, e, data){
51923         if(data.header != n){
51924             this.positionIndicator(data.header, n, e);
51925         }
51926     },
51927
51928     onNodeOver : function(n, dd, e, data){
51929         var result = false;
51930         if(data.header != n){
51931             result = this.positionIndicator(data.header, n, e);
51932         }
51933         if(!result){
51934             this.proxyTop.hide();
51935             this.proxyBottom.hide();
51936         }
51937         return result ? this.dropAllowed : this.dropNotAllowed;
51938     },
51939
51940     onNodeOut : function(n, dd, e, data){
51941         this.proxyTop.hide();
51942         this.proxyBottom.hide();
51943     },
51944
51945     onNodeDrop : function(n, dd, e, data){
51946         var h = data.header;
51947         if(h != n){
51948             var cm = this.grid.colModel;
51949             var x = Roo.lib.Event.getPageX(e);
51950             var r = Roo.lib.Dom.getRegion(n.firstChild);
51951             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
51952             var oldIndex = this.view.getCellIndex(h);
51953             var newIndex = this.view.getCellIndex(n);
51954             var locked = cm.isLocked(newIndex);
51955             if(pt == "after"){
51956                 newIndex++;
51957             }
51958             if(oldIndex < newIndex){
51959                 newIndex--;
51960             }
51961             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
51962                 return false;
51963             }
51964             cm.setLocked(oldIndex, locked, true);
51965             cm.moveColumn(oldIndex, newIndex);
51966             this.grid.fireEvent("columnmove", oldIndex, newIndex);
51967             return true;
51968         }
51969         return false;
51970     }
51971 });
51972 /*
51973  * Based on:
51974  * Ext JS Library 1.1.1
51975  * Copyright(c) 2006-2007, Ext JS, LLC.
51976  *
51977  * Originally Released Under LGPL - original licence link has changed is not relivant.
51978  *
51979  * Fork - LGPL
51980  * <script type="text/javascript">
51981  */
51982   
51983 /**
51984  * @class Roo.grid.GridView
51985  * @extends Roo.util.Observable
51986  *
51987  * @constructor
51988  * @param {Object} config
51989  */
51990 Roo.grid.GridView = function(config){
51991     Roo.grid.GridView.superclass.constructor.call(this);
51992     this.el = null;
51993
51994     Roo.apply(this, config);
51995 };
51996
51997 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
51998
51999     unselectable :  'unselectable="on"',
52000     unselectableCls :  'x-unselectable',
52001     
52002     
52003     rowClass : "x-grid-row",
52004
52005     cellClass : "x-grid-col",
52006
52007     tdClass : "x-grid-td",
52008
52009     hdClass : "x-grid-hd",
52010
52011     splitClass : "x-grid-split",
52012
52013     sortClasses : ["sort-asc", "sort-desc"],
52014
52015     enableMoveAnim : false,
52016
52017     hlColor: "C3DAF9",
52018
52019     dh : Roo.DomHelper,
52020
52021     fly : Roo.Element.fly,
52022
52023     css : Roo.util.CSS,
52024
52025     borderWidth: 1,
52026
52027     splitOffset: 3,
52028
52029     scrollIncrement : 22,
52030
52031     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52032
52033     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52034
52035     bind : function(ds, cm){
52036         if(this.ds){
52037             this.ds.un("load", this.onLoad, this);
52038             this.ds.un("datachanged", this.onDataChange, this);
52039             this.ds.un("add", this.onAdd, this);
52040             this.ds.un("remove", this.onRemove, this);
52041             this.ds.un("update", this.onUpdate, this);
52042             this.ds.un("clear", this.onClear, this);
52043         }
52044         if(ds){
52045             ds.on("load", this.onLoad, this);
52046             ds.on("datachanged", this.onDataChange, this);
52047             ds.on("add", this.onAdd, this);
52048             ds.on("remove", this.onRemove, this);
52049             ds.on("update", this.onUpdate, this);
52050             ds.on("clear", this.onClear, this);
52051         }
52052         this.ds = ds;
52053
52054         if(this.cm){
52055             this.cm.un("widthchange", this.onColWidthChange, this);
52056             this.cm.un("headerchange", this.onHeaderChange, this);
52057             this.cm.un("hiddenchange", this.onHiddenChange, this);
52058             this.cm.un("columnmoved", this.onColumnMove, this);
52059             this.cm.un("columnlockchange", this.onColumnLock, this);
52060         }
52061         if(cm){
52062             this.generateRules(cm);
52063             cm.on("widthchange", this.onColWidthChange, this);
52064             cm.on("headerchange", this.onHeaderChange, this);
52065             cm.on("hiddenchange", this.onHiddenChange, this);
52066             cm.on("columnmoved", this.onColumnMove, this);
52067             cm.on("columnlockchange", this.onColumnLock, this);
52068         }
52069         this.cm = cm;
52070     },
52071
52072     init: function(grid){
52073         Roo.grid.GridView.superclass.init.call(this, grid);
52074
52075         this.bind(grid.dataSource, grid.colModel);
52076
52077         grid.on("headerclick", this.handleHeaderClick, this);
52078
52079         if(grid.trackMouseOver){
52080             grid.on("mouseover", this.onRowOver, this);
52081             grid.on("mouseout", this.onRowOut, this);
52082         }
52083         grid.cancelTextSelection = function(){};
52084         this.gridId = grid.id;
52085
52086         var tpls = this.templates || {};
52087
52088         if(!tpls.master){
52089             tpls.master = new Roo.Template(
52090                '<div class="x-grid" hidefocus="true">',
52091                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52092                   '<div class="x-grid-topbar"></div>',
52093                   '<div class="x-grid-scroller"><div></div></div>',
52094                   '<div class="x-grid-locked">',
52095                       '<div class="x-grid-header">{lockedHeader}</div>',
52096                       '<div class="x-grid-body">{lockedBody}</div>',
52097                   "</div>",
52098                   '<div class="x-grid-viewport">',
52099                       '<div class="x-grid-header">{header}</div>',
52100                       '<div class="x-grid-body">{body}</div>',
52101                   "</div>",
52102                   '<div class="x-grid-bottombar"></div>',
52103                  
52104                   '<div class="x-grid-resize-proxy">&#160;</div>',
52105                "</div>"
52106             );
52107             tpls.master.disableformats = true;
52108         }
52109
52110         if(!tpls.header){
52111             tpls.header = new Roo.Template(
52112                '<table border="0" cellspacing="0" cellpadding="0">',
52113                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52114                "</table>{splits}"
52115             );
52116             tpls.header.disableformats = true;
52117         }
52118         tpls.header.compile();
52119
52120         if(!tpls.hcell){
52121             tpls.hcell = new Roo.Template(
52122                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52123                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52124                 "</div></td>"
52125              );
52126              tpls.hcell.disableFormats = true;
52127         }
52128         tpls.hcell.compile();
52129
52130         if(!tpls.hsplit){
52131             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52132                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52133             tpls.hsplit.disableFormats = true;
52134         }
52135         tpls.hsplit.compile();
52136
52137         if(!tpls.body){
52138             tpls.body = new Roo.Template(
52139                '<table border="0" cellspacing="0" cellpadding="0">',
52140                "<tbody>{rows}</tbody>",
52141                "</table>"
52142             );
52143             tpls.body.disableFormats = true;
52144         }
52145         tpls.body.compile();
52146
52147         if(!tpls.row){
52148             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52149             tpls.row.disableFormats = true;
52150         }
52151         tpls.row.compile();
52152
52153         if(!tpls.cell){
52154             tpls.cell = new Roo.Template(
52155                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52156                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52157                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52158                 "</td>"
52159             );
52160             tpls.cell.disableFormats = true;
52161         }
52162         tpls.cell.compile();
52163
52164         this.templates = tpls;
52165     },
52166
52167     // remap these for backwards compat
52168     onColWidthChange : function(){
52169         this.updateColumns.apply(this, arguments);
52170     },
52171     onHeaderChange : function(){
52172         this.updateHeaders.apply(this, arguments);
52173     }, 
52174     onHiddenChange : function(){
52175         this.handleHiddenChange.apply(this, arguments);
52176     },
52177     onColumnMove : function(){
52178         this.handleColumnMove.apply(this, arguments);
52179     },
52180     onColumnLock : function(){
52181         this.handleLockChange.apply(this, arguments);
52182     },
52183
52184     onDataChange : function(){
52185         this.refresh();
52186         this.updateHeaderSortState();
52187     },
52188
52189     onClear : function(){
52190         this.refresh();
52191     },
52192
52193     onUpdate : function(ds, record){
52194         this.refreshRow(record);
52195     },
52196
52197     refreshRow : function(record){
52198         var ds = this.ds, index;
52199         if(typeof record == 'number'){
52200             index = record;
52201             record = ds.getAt(index);
52202         }else{
52203             index = ds.indexOf(record);
52204         }
52205         this.insertRows(ds, index, index, true);
52206         this.onRemove(ds, record, index+1, true);
52207         this.syncRowHeights(index, index);
52208         this.layout();
52209         this.fireEvent("rowupdated", this, index, record);
52210     },
52211
52212     onAdd : function(ds, records, index){
52213         this.insertRows(ds, index, index + (records.length-1));
52214     },
52215
52216     onRemove : function(ds, record, index, isUpdate){
52217         if(isUpdate !== true){
52218             this.fireEvent("beforerowremoved", this, index, record);
52219         }
52220         var bt = this.getBodyTable(), lt = this.getLockedTable();
52221         if(bt.rows[index]){
52222             bt.firstChild.removeChild(bt.rows[index]);
52223         }
52224         if(lt.rows[index]){
52225             lt.firstChild.removeChild(lt.rows[index]);
52226         }
52227         if(isUpdate !== true){
52228             this.stripeRows(index);
52229             this.syncRowHeights(index, index);
52230             this.layout();
52231             this.fireEvent("rowremoved", this, index, record);
52232         }
52233     },
52234
52235     onLoad : function(){
52236         this.scrollToTop();
52237     },
52238
52239     /**
52240      * Scrolls the grid to the top
52241      */
52242     scrollToTop : function(){
52243         if(this.scroller){
52244             this.scroller.dom.scrollTop = 0;
52245             this.syncScroll();
52246         }
52247     },
52248
52249     /**
52250      * Gets a panel in the header of the grid that can be used for toolbars etc.
52251      * After modifying the contents of this panel a call to grid.autoSize() may be
52252      * required to register any changes in size.
52253      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52254      * @return Roo.Element
52255      */
52256     getHeaderPanel : function(doShow){
52257         if(doShow){
52258             this.headerPanel.show();
52259         }
52260         return this.headerPanel;
52261     },
52262
52263     /**
52264      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52265      * After modifying the contents of this panel a call to grid.autoSize() may be
52266      * required to register any changes in size.
52267      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52268      * @return Roo.Element
52269      */
52270     getFooterPanel : function(doShow){
52271         if(doShow){
52272             this.footerPanel.show();
52273         }
52274         return this.footerPanel;
52275     },
52276
52277     initElements : function(){
52278         var E = Roo.Element;
52279         var el = this.grid.getGridEl().dom.firstChild;
52280         var cs = el.childNodes;
52281
52282         this.el = new E(el);
52283         
52284          this.focusEl = new E(el.firstChild);
52285         this.focusEl.swallowEvent("click", true);
52286         
52287         this.headerPanel = new E(cs[1]);
52288         this.headerPanel.enableDisplayMode("block");
52289
52290         this.scroller = new E(cs[2]);
52291         this.scrollSizer = new E(this.scroller.dom.firstChild);
52292
52293         this.lockedWrap = new E(cs[3]);
52294         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52295         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52296
52297         this.mainWrap = new E(cs[4]);
52298         this.mainHd = new E(this.mainWrap.dom.firstChild);
52299         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52300
52301         this.footerPanel = new E(cs[5]);
52302         this.footerPanel.enableDisplayMode("block");
52303
52304         this.resizeProxy = new E(cs[6]);
52305
52306         this.headerSelector = String.format(
52307            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52308            this.lockedHd.id, this.mainHd.id
52309         );
52310
52311         this.splitterSelector = String.format(
52312            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52313            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52314         );
52315     },
52316     idToCssName : function(s)
52317     {
52318         return s.replace(/[^a-z0-9]+/ig, '-');
52319     },
52320
52321     getHeaderCell : function(index){
52322         return Roo.DomQuery.select(this.headerSelector)[index];
52323     },
52324
52325     getHeaderCellMeasure : function(index){
52326         return this.getHeaderCell(index).firstChild;
52327     },
52328
52329     getHeaderCellText : function(index){
52330         return this.getHeaderCell(index).firstChild.firstChild;
52331     },
52332
52333     getLockedTable : function(){
52334         return this.lockedBody.dom.firstChild;
52335     },
52336
52337     getBodyTable : function(){
52338         return this.mainBody.dom.firstChild;
52339     },
52340
52341     getLockedRow : function(index){
52342         return this.getLockedTable().rows[index];
52343     },
52344
52345     getRow : function(index){
52346         return this.getBodyTable().rows[index];
52347     },
52348
52349     getRowComposite : function(index){
52350         if(!this.rowEl){
52351             this.rowEl = new Roo.CompositeElementLite();
52352         }
52353         var els = [], lrow, mrow;
52354         if(lrow = this.getLockedRow(index)){
52355             els.push(lrow);
52356         }
52357         if(mrow = this.getRow(index)){
52358             els.push(mrow);
52359         }
52360         this.rowEl.elements = els;
52361         return this.rowEl;
52362     },
52363     /**
52364      * Gets the 'td' of the cell
52365      * 
52366      * @param {Integer} rowIndex row to select
52367      * @param {Integer} colIndex column to select
52368      * 
52369      * @return {Object} 
52370      */
52371     getCell : function(rowIndex, colIndex){
52372         var locked = this.cm.getLockedCount();
52373         var source;
52374         if(colIndex < locked){
52375             source = this.lockedBody.dom.firstChild;
52376         }else{
52377             source = this.mainBody.dom.firstChild;
52378             colIndex -= locked;
52379         }
52380         return source.rows[rowIndex].childNodes[colIndex];
52381     },
52382
52383     getCellText : function(rowIndex, colIndex){
52384         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52385     },
52386
52387     getCellBox : function(cell){
52388         var b = this.fly(cell).getBox();
52389         if(Roo.isOpera){ // opera fails to report the Y
52390             b.y = cell.offsetTop + this.mainBody.getY();
52391         }
52392         return b;
52393     },
52394
52395     getCellIndex : function(cell){
52396         var id = String(cell.className).match(this.cellRE);
52397         if(id){
52398             return parseInt(id[1], 10);
52399         }
52400         return 0;
52401     },
52402
52403     findHeaderIndex : function(n){
52404         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52405         return r ? this.getCellIndex(r) : false;
52406     },
52407
52408     findHeaderCell : function(n){
52409         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52410         return r ? r : false;
52411     },
52412
52413     findRowIndex : function(n){
52414         if(!n){
52415             return false;
52416         }
52417         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52418         return r ? r.rowIndex : false;
52419     },
52420
52421     findCellIndex : function(node){
52422         var stop = this.el.dom;
52423         while(node && node != stop){
52424             if(this.findRE.test(node.className)){
52425                 return this.getCellIndex(node);
52426             }
52427             node = node.parentNode;
52428         }
52429         return false;
52430     },
52431
52432     getColumnId : function(index){
52433         return this.cm.getColumnId(index);
52434     },
52435
52436     getSplitters : function()
52437     {
52438         if(this.splitterSelector){
52439            return Roo.DomQuery.select(this.splitterSelector);
52440         }else{
52441             return null;
52442       }
52443     },
52444
52445     getSplitter : function(index){
52446         return this.getSplitters()[index];
52447     },
52448
52449     onRowOver : function(e, t){
52450         var row;
52451         if((row = this.findRowIndex(t)) !== false){
52452             this.getRowComposite(row).addClass("x-grid-row-over");
52453         }
52454     },
52455
52456     onRowOut : function(e, t){
52457         var row;
52458         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52459             this.getRowComposite(row).removeClass("x-grid-row-over");
52460         }
52461     },
52462
52463     renderHeaders : function(){
52464         var cm = this.cm;
52465         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52466         var cb = [], lb = [], sb = [], lsb = [], p = {};
52467         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52468             p.cellId = "x-grid-hd-0-" + i;
52469             p.splitId = "x-grid-csplit-0-" + i;
52470             p.id = cm.getColumnId(i);
52471             p.title = cm.getColumnTooltip(i) || "";
52472             p.value = cm.getColumnHeader(i) || "";
52473             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52474             if(!cm.isLocked(i)){
52475                 cb[cb.length] = ct.apply(p);
52476                 sb[sb.length] = st.apply(p);
52477             }else{
52478                 lb[lb.length] = ct.apply(p);
52479                 lsb[lsb.length] = st.apply(p);
52480             }
52481         }
52482         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52483                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52484     },
52485
52486     updateHeaders : function(){
52487         var html = this.renderHeaders();
52488         this.lockedHd.update(html[0]);
52489         this.mainHd.update(html[1]);
52490     },
52491
52492     /**
52493      * Focuses the specified row.
52494      * @param {Number} row The row index
52495      */
52496     focusRow : function(row)
52497     {
52498         //Roo.log('GridView.focusRow');
52499         var x = this.scroller.dom.scrollLeft;
52500         this.focusCell(row, 0, false);
52501         this.scroller.dom.scrollLeft = x;
52502     },
52503
52504     /**
52505      * Focuses the specified cell.
52506      * @param {Number} row The row index
52507      * @param {Number} col The column index
52508      * @param {Boolean} hscroll false to disable horizontal scrolling
52509      */
52510     focusCell : function(row, col, hscroll)
52511     {
52512         //Roo.log('GridView.focusCell');
52513         var el = this.ensureVisible(row, col, hscroll);
52514         this.focusEl.alignTo(el, "tl-tl");
52515         if(Roo.isGecko){
52516             this.focusEl.focus();
52517         }else{
52518             this.focusEl.focus.defer(1, this.focusEl);
52519         }
52520     },
52521
52522     /**
52523      * Scrolls the specified cell into view
52524      * @param {Number} row The row index
52525      * @param {Number} col The column index
52526      * @param {Boolean} hscroll false to disable horizontal scrolling
52527      */
52528     ensureVisible : function(row, col, hscroll)
52529     {
52530         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52531         //return null; //disable for testing.
52532         if(typeof row != "number"){
52533             row = row.rowIndex;
52534         }
52535         if(row < 0 && row >= this.ds.getCount()){
52536             return  null;
52537         }
52538         col = (col !== undefined ? col : 0);
52539         var cm = this.grid.colModel;
52540         while(cm.isHidden(col)){
52541             col++;
52542         }
52543
52544         var el = this.getCell(row, col);
52545         if(!el){
52546             return null;
52547         }
52548         var c = this.scroller.dom;
52549
52550         var ctop = parseInt(el.offsetTop, 10);
52551         var cleft = parseInt(el.offsetLeft, 10);
52552         var cbot = ctop + el.offsetHeight;
52553         var cright = cleft + el.offsetWidth;
52554         
52555         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52556         var stop = parseInt(c.scrollTop, 10);
52557         var sleft = parseInt(c.scrollLeft, 10);
52558         var sbot = stop + ch;
52559         var sright = sleft + c.clientWidth;
52560         /*
52561         Roo.log('GridView.ensureVisible:' +
52562                 ' ctop:' + ctop +
52563                 ' c.clientHeight:' + c.clientHeight +
52564                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52565                 ' stop:' + stop +
52566                 ' cbot:' + cbot +
52567                 ' sbot:' + sbot +
52568                 ' ch:' + ch  
52569                 );
52570         */
52571         if(ctop < stop){
52572              c.scrollTop = ctop;
52573             //Roo.log("set scrolltop to ctop DISABLE?");
52574         }else if(cbot > sbot){
52575             //Roo.log("set scrolltop to cbot-ch");
52576             c.scrollTop = cbot-ch;
52577         }
52578         
52579         if(hscroll !== false){
52580             if(cleft < sleft){
52581                 c.scrollLeft = cleft;
52582             }else if(cright > sright){
52583                 c.scrollLeft = cright-c.clientWidth;
52584             }
52585         }
52586          
52587         return el;
52588     },
52589
52590     updateColumns : function(){
52591         this.grid.stopEditing();
52592         var cm = this.grid.colModel, colIds = this.getColumnIds();
52593         //var totalWidth = cm.getTotalWidth();
52594         var pos = 0;
52595         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52596             //if(cm.isHidden(i)) continue;
52597             var w = cm.getColumnWidth(i);
52598             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52599             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52600         }
52601         this.updateSplitters();
52602     },
52603
52604     generateRules : function(cm){
52605         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52606         Roo.util.CSS.removeStyleSheet(rulesId);
52607         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52608             var cid = cm.getColumnId(i);
52609             var align = '';
52610             if(cm.config[i].align){
52611                 align = 'text-align:'+cm.config[i].align+';';
52612             }
52613             var hidden = '';
52614             if(cm.isHidden(i)){
52615                 hidden = 'display:none;';
52616             }
52617             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52618             ruleBuf.push(
52619                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52620                     this.hdSelector, cid, " {\n", align, width, "}\n",
52621                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52622                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52623         }
52624         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52625     },
52626
52627     updateSplitters : function(){
52628         var cm = this.cm, s = this.getSplitters();
52629         if(s){ // splitters not created yet
52630             var pos = 0, locked = true;
52631             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52632                 if(cm.isHidden(i)) continue;
52633                 var w = cm.getColumnWidth(i); // make sure it's a number
52634                 if(!cm.isLocked(i) && locked){
52635                     pos = 0;
52636                     locked = false;
52637                 }
52638                 pos += w;
52639                 s[i].style.left = (pos-this.splitOffset) + "px";
52640             }
52641         }
52642     },
52643
52644     handleHiddenChange : function(colModel, colIndex, hidden){
52645         if(hidden){
52646             this.hideColumn(colIndex);
52647         }else{
52648             this.unhideColumn(colIndex);
52649         }
52650     },
52651
52652     hideColumn : function(colIndex){
52653         var cid = this.getColumnId(colIndex);
52654         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52655         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52656         if(Roo.isSafari){
52657             this.updateHeaders();
52658         }
52659         this.updateSplitters();
52660         this.layout();
52661     },
52662
52663     unhideColumn : function(colIndex){
52664         var cid = this.getColumnId(colIndex);
52665         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52666         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52667
52668         if(Roo.isSafari){
52669             this.updateHeaders();
52670         }
52671         this.updateSplitters();
52672         this.layout();
52673     },
52674
52675     insertRows : function(dm, firstRow, lastRow, isUpdate){
52676         if(firstRow == 0 && lastRow == dm.getCount()-1){
52677             this.refresh();
52678         }else{
52679             if(!isUpdate){
52680                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52681             }
52682             var s = this.getScrollState();
52683             var markup = this.renderRows(firstRow, lastRow);
52684             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52685             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52686             this.restoreScroll(s);
52687             if(!isUpdate){
52688                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52689                 this.syncRowHeights(firstRow, lastRow);
52690                 this.stripeRows(firstRow);
52691                 this.layout();
52692             }
52693         }
52694     },
52695
52696     bufferRows : function(markup, target, index){
52697         var before = null, trows = target.rows, tbody = target.tBodies[0];
52698         if(index < trows.length){
52699             before = trows[index];
52700         }
52701         var b = document.createElement("div");
52702         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52703         var rows = b.firstChild.rows;
52704         for(var i = 0, len = rows.length; i < len; i++){
52705             if(before){
52706                 tbody.insertBefore(rows[0], before);
52707             }else{
52708                 tbody.appendChild(rows[0]);
52709             }
52710         }
52711         b.innerHTML = "";
52712         b = null;
52713     },
52714
52715     deleteRows : function(dm, firstRow, lastRow){
52716         if(dm.getRowCount()<1){
52717             this.fireEvent("beforerefresh", this);
52718             this.mainBody.update("");
52719             this.lockedBody.update("");
52720             this.fireEvent("refresh", this);
52721         }else{
52722             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52723             var bt = this.getBodyTable();
52724             var tbody = bt.firstChild;
52725             var rows = bt.rows;
52726             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52727                 tbody.removeChild(rows[firstRow]);
52728             }
52729             this.stripeRows(firstRow);
52730             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52731         }
52732     },
52733
52734     updateRows : function(dataSource, firstRow, lastRow){
52735         var s = this.getScrollState();
52736         this.refresh();
52737         this.restoreScroll(s);
52738     },
52739
52740     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52741         if(!noRefresh){
52742            this.refresh();
52743         }
52744         this.updateHeaderSortState();
52745     },
52746
52747     getScrollState : function(){
52748         
52749         var sb = this.scroller.dom;
52750         return {left: sb.scrollLeft, top: sb.scrollTop};
52751     },
52752
52753     stripeRows : function(startRow){
52754         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52755             return;
52756         }
52757         startRow = startRow || 0;
52758         var rows = this.getBodyTable().rows;
52759         var lrows = this.getLockedTable().rows;
52760         var cls = ' x-grid-row-alt ';
52761         for(var i = startRow, len = rows.length; i < len; i++){
52762             var row = rows[i], lrow = lrows[i];
52763             var isAlt = ((i+1) % 2 == 0);
52764             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52765             if(isAlt == hasAlt){
52766                 continue;
52767             }
52768             if(isAlt){
52769                 row.className += " x-grid-row-alt";
52770             }else{
52771                 row.className = row.className.replace("x-grid-row-alt", "");
52772             }
52773             if(lrow){
52774                 lrow.className = row.className;
52775             }
52776         }
52777     },
52778
52779     restoreScroll : function(state){
52780         //Roo.log('GridView.restoreScroll');
52781         var sb = this.scroller.dom;
52782         sb.scrollLeft = state.left;
52783         sb.scrollTop = state.top;
52784         this.syncScroll();
52785     },
52786
52787     syncScroll : function(){
52788         //Roo.log('GridView.syncScroll');
52789         var sb = this.scroller.dom;
52790         var sh = this.mainHd.dom;
52791         var bs = this.mainBody.dom;
52792         var lv = this.lockedBody.dom;
52793         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52794         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52795     },
52796
52797     handleScroll : function(e){
52798         this.syncScroll();
52799         var sb = this.scroller.dom;
52800         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52801         e.stopEvent();
52802     },
52803
52804     handleWheel : function(e){
52805         var d = e.getWheelDelta();
52806         this.scroller.dom.scrollTop -= d*22;
52807         // set this here to prevent jumpy scrolling on large tables
52808         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52809         e.stopEvent();
52810     },
52811
52812     renderRows : function(startRow, endRow){
52813         // pull in all the crap needed to render rows
52814         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52815         var colCount = cm.getColumnCount();
52816
52817         if(ds.getCount() < 1){
52818             return ["", ""];
52819         }
52820
52821         // build a map for all the columns
52822         var cs = [];
52823         for(var i = 0; i < colCount; i++){
52824             var name = cm.getDataIndex(i);
52825             cs[i] = {
52826                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52827                 renderer : cm.getRenderer(i),
52828                 id : cm.getColumnId(i),
52829                 locked : cm.isLocked(i)
52830             };
52831         }
52832
52833         startRow = startRow || 0;
52834         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52835
52836         // records to render
52837         var rs = ds.getRange(startRow, endRow);
52838
52839         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52840     },
52841
52842     // As much as I hate to duplicate code, this was branched because FireFox really hates
52843     // [].join("") on strings. The performance difference was substantial enough to
52844     // branch this function
52845     doRender : Roo.isGecko ?
52846             function(cs, rs, ds, startRow, colCount, stripe){
52847                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52848                 // buffers
52849                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52850                 
52851                 var hasListener = this.grid.hasListener('rowclass');
52852                 var rowcfg = {};
52853                 for(var j = 0, len = rs.length; j < len; j++){
52854                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52855                     for(var i = 0; i < colCount; i++){
52856                         c = cs[i];
52857                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52858                         p.id = c.id;
52859                         p.css = p.attr = "";
52860                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52861                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52862                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52863                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52864                         }
52865                         var markup = ct.apply(p);
52866                         if(!c.locked){
52867                             cb+= markup;
52868                         }else{
52869                             lcb+= markup;
52870                         }
52871                     }
52872                     var alt = [];
52873                     if(stripe && ((rowIndex+1) % 2 == 0)){
52874                         alt.push("x-grid-row-alt")
52875                     }
52876                     if(r.dirty){
52877                         alt.push(  " x-grid-dirty-row");
52878                     }
52879                     rp.cells = lcb;
52880                     if(this.getRowClass){
52881                         alt.push(this.getRowClass(r, rowIndex));
52882                     }
52883                     if (hasListener) {
52884                         rowcfg = {
52885                              
52886                             record: r,
52887                             rowIndex : rowIndex,
52888                             rowClass : ''
52889                         }
52890                         this.grid.fireEvent('rowclass', this, rowcfg);
52891                         alt.push(rowcfg.rowClass);
52892                     }
52893                     rp.alt = alt.join(" ");
52894                     lbuf+= rt.apply(rp);
52895                     rp.cells = cb;
52896                     buf+=  rt.apply(rp);
52897                 }
52898                 return [lbuf, buf];
52899             } :
52900             function(cs, rs, ds, startRow, colCount, stripe){
52901                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52902                 // buffers
52903                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52904                 var hasListener = this.grid.hasListener('rowclass');
52905  
52906                 var rowcfg = {};
52907                 for(var j = 0, len = rs.length; j < len; j++){
52908                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52909                     for(var i = 0; i < colCount; i++){
52910                         c = cs[i];
52911                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52912                         p.id = c.id;
52913                         p.css = p.attr = "";
52914                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52915                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52916                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52917                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52918                         }
52919                         
52920                         var markup = ct.apply(p);
52921                         if(!c.locked){
52922                             cb[cb.length] = markup;
52923                         }else{
52924                             lcb[lcb.length] = markup;
52925                         }
52926                     }
52927                     var alt = [];
52928                     if(stripe && ((rowIndex+1) % 2 == 0)){
52929                         alt.push( "x-grid-row-alt");
52930                     }
52931                     if(r.dirty){
52932                         alt.push(" x-grid-dirty-row");
52933                     }
52934                     rp.cells = lcb;
52935                     if(this.getRowClass){
52936                         alt.push( this.getRowClass(r, rowIndex));
52937                     }
52938                     if (hasListener) {
52939                         rowcfg = {
52940                              
52941                             record: r,
52942                             rowIndex : rowIndex,
52943                             rowClass : ''
52944                         }
52945                         this.grid.fireEvent('rowclass', this, rowcfg);
52946                         alt.push(rowcfg.rowClass);
52947                     }
52948                     rp.alt = alt.join(" ");
52949                     rp.cells = lcb.join("");
52950                     lbuf[lbuf.length] = rt.apply(rp);
52951                     rp.cells = cb.join("");
52952                     buf[buf.length] =  rt.apply(rp);
52953                 }
52954                 return [lbuf.join(""), buf.join("")];
52955             },
52956
52957     renderBody : function(){
52958         var markup = this.renderRows();
52959         var bt = this.templates.body;
52960         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
52961     },
52962
52963     /**
52964      * Refreshes the grid
52965      * @param {Boolean} headersToo
52966      */
52967     refresh : function(headersToo){
52968         this.fireEvent("beforerefresh", this);
52969         this.grid.stopEditing();
52970         var result = this.renderBody();
52971         this.lockedBody.update(result[0]);
52972         this.mainBody.update(result[1]);
52973         if(headersToo === true){
52974             this.updateHeaders();
52975             this.updateColumns();
52976             this.updateSplitters();
52977             this.updateHeaderSortState();
52978         }
52979         this.syncRowHeights();
52980         this.layout();
52981         this.fireEvent("refresh", this);
52982     },
52983
52984     handleColumnMove : function(cm, oldIndex, newIndex){
52985         this.indexMap = null;
52986         var s = this.getScrollState();
52987         this.refresh(true);
52988         this.restoreScroll(s);
52989         this.afterMove(newIndex);
52990     },
52991
52992     afterMove : function(colIndex){
52993         if(this.enableMoveAnim && Roo.enableFx){
52994             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
52995         }
52996         // if multisort - fix sortOrder, and reload..
52997         if (this.grid.dataSource.multiSort) {
52998             // the we can call sort again..
52999             var dm = this.grid.dataSource;
53000             var cm = this.grid.colModel;
53001             var so = [];
53002             for(var i = 0; i < cm.config.length; i++ ) {
53003                 
53004                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53005                     continue; // dont' bother, it's not in sort list or being set.
53006                 }
53007                 
53008                 so.push(cm.config[i].dataIndex);
53009             };
53010             dm.sortOrder = so;
53011             dm.load(dm.lastOptions);
53012             
53013             
53014         }
53015         
53016     },
53017
53018     updateCell : function(dm, rowIndex, dataIndex){
53019         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53020         if(typeof colIndex == "undefined"){ // not present in grid
53021             return;
53022         }
53023         var cm = this.grid.colModel;
53024         var cell = this.getCell(rowIndex, colIndex);
53025         var cellText = this.getCellText(rowIndex, colIndex);
53026
53027         var p = {
53028             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53029             id : cm.getColumnId(colIndex),
53030             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53031         };
53032         var renderer = cm.getRenderer(colIndex);
53033         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53034         if(typeof val == "undefined" || val === "") val = "&#160;";
53035         cellText.innerHTML = val;
53036         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53037         this.syncRowHeights(rowIndex, rowIndex);
53038     },
53039
53040     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53041         var maxWidth = 0;
53042         if(this.grid.autoSizeHeaders){
53043             var h = this.getHeaderCellMeasure(colIndex);
53044             maxWidth = Math.max(maxWidth, h.scrollWidth);
53045         }
53046         var tb, index;
53047         if(this.cm.isLocked(colIndex)){
53048             tb = this.getLockedTable();
53049             index = colIndex;
53050         }else{
53051             tb = this.getBodyTable();
53052             index = colIndex - this.cm.getLockedCount();
53053         }
53054         if(tb && tb.rows){
53055             var rows = tb.rows;
53056             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53057             for(var i = 0; i < stopIndex; i++){
53058                 var cell = rows[i].childNodes[index].firstChild;
53059                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53060             }
53061         }
53062         return maxWidth + /*margin for error in IE*/ 5;
53063     },
53064     /**
53065      * Autofit a column to its content.
53066      * @param {Number} colIndex
53067      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53068      */
53069      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53070          if(this.cm.isHidden(colIndex)){
53071              return; // can't calc a hidden column
53072          }
53073         if(forceMinSize){
53074             var cid = this.cm.getColumnId(colIndex);
53075             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53076            if(this.grid.autoSizeHeaders){
53077                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53078            }
53079         }
53080         var newWidth = this.calcColumnWidth(colIndex);
53081         this.cm.setColumnWidth(colIndex,
53082             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53083         if(!suppressEvent){
53084             this.grid.fireEvent("columnresize", colIndex, newWidth);
53085         }
53086     },
53087
53088     /**
53089      * Autofits all columns to their content and then expands to fit any extra space in the grid
53090      */
53091      autoSizeColumns : function(){
53092         var cm = this.grid.colModel;
53093         var colCount = cm.getColumnCount();
53094         for(var i = 0; i < colCount; i++){
53095             this.autoSizeColumn(i, true, true);
53096         }
53097         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53098             this.fitColumns();
53099         }else{
53100             this.updateColumns();
53101             this.layout();
53102         }
53103     },
53104
53105     /**
53106      * Autofits all columns to the grid's width proportionate with their current size
53107      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53108      */
53109     fitColumns : function(reserveScrollSpace){
53110         var cm = this.grid.colModel;
53111         var colCount = cm.getColumnCount();
53112         var cols = [];
53113         var width = 0;
53114         var i, w;
53115         for (i = 0; i < colCount; i++){
53116             if(!cm.isHidden(i) && !cm.isFixed(i)){
53117                 w = cm.getColumnWidth(i);
53118                 cols.push(i);
53119                 cols.push(w);
53120                 width += w;
53121             }
53122         }
53123         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53124         if(reserveScrollSpace){
53125             avail -= 17;
53126         }
53127         var frac = (avail - cm.getTotalWidth())/width;
53128         while (cols.length){
53129             w = cols.pop();
53130             i = cols.pop();
53131             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53132         }
53133         this.updateColumns();
53134         this.layout();
53135     },
53136
53137     onRowSelect : function(rowIndex){
53138         var row = this.getRowComposite(rowIndex);
53139         row.addClass("x-grid-row-selected");
53140     },
53141
53142     onRowDeselect : function(rowIndex){
53143         var row = this.getRowComposite(rowIndex);
53144         row.removeClass("x-grid-row-selected");
53145     },
53146
53147     onCellSelect : function(row, col){
53148         var cell = this.getCell(row, col);
53149         if(cell){
53150             Roo.fly(cell).addClass("x-grid-cell-selected");
53151         }
53152     },
53153
53154     onCellDeselect : function(row, col){
53155         var cell = this.getCell(row, col);
53156         if(cell){
53157             Roo.fly(cell).removeClass("x-grid-cell-selected");
53158         }
53159     },
53160
53161     updateHeaderSortState : function(){
53162         
53163         // sort state can be single { field: xxx, direction : yyy}
53164         // or   { xxx=>ASC , yyy : DESC ..... }
53165         
53166         var mstate = {};
53167         if (!this.ds.multiSort) { 
53168             var state = this.ds.getSortState();
53169             if(!state){
53170                 return;
53171             }
53172             mstate[state.field] = state.direction;
53173             // FIXME... - this is not used here.. but might be elsewhere..
53174             this.sortState = state;
53175             
53176         } else {
53177             mstate = this.ds.sortToggle;
53178         }
53179         //remove existing sort classes..
53180         
53181         var sc = this.sortClasses;
53182         var hds = this.el.select(this.headerSelector).removeClass(sc);
53183         
53184         for(var f in mstate) {
53185         
53186             var sortColumn = this.cm.findColumnIndex(f);
53187             
53188             if(sortColumn != -1){
53189                 var sortDir = mstate[f];        
53190                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53191             }
53192         }
53193         
53194          
53195         
53196     },
53197
53198
53199     handleHeaderClick : function(g, index){
53200         if(this.headersDisabled){
53201             return;
53202         }
53203         var dm = g.dataSource, cm = g.colModel;
53204         if(!cm.isSortable(index)){
53205             return;
53206         }
53207         g.stopEditing();
53208         
53209         if (dm.multiSort) {
53210             // update the sortOrder
53211             var so = [];
53212             for(var i = 0; i < cm.config.length; i++ ) {
53213                 
53214                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53215                     continue; // dont' bother, it's not in sort list or being set.
53216                 }
53217                 
53218                 so.push(cm.config[i].dataIndex);
53219             };
53220             dm.sortOrder = so;
53221         }
53222         
53223         
53224         dm.sort(cm.getDataIndex(index));
53225     },
53226
53227
53228     destroy : function(){
53229         if(this.colMenu){
53230             this.colMenu.removeAll();
53231             Roo.menu.MenuMgr.unregister(this.colMenu);
53232             this.colMenu.getEl().remove();
53233             delete this.colMenu;
53234         }
53235         if(this.hmenu){
53236             this.hmenu.removeAll();
53237             Roo.menu.MenuMgr.unregister(this.hmenu);
53238             this.hmenu.getEl().remove();
53239             delete this.hmenu;
53240         }
53241         if(this.grid.enableColumnMove){
53242             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53243             if(dds){
53244                 for(var dd in dds){
53245                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53246                         var elid = dds[dd].dragElId;
53247                         dds[dd].unreg();
53248                         Roo.get(elid).remove();
53249                     } else if(dds[dd].config.isTarget){
53250                         dds[dd].proxyTop.remove();
53251                         dds[dd].proxyBottom.remove();
53252                         dds[dd].unreg();
53253                     }
53254                     if(Roo.dd.DDM.locationCache[dd]){
53255                         delete Roo.dd.DDM.locationCache[dd];
53256                     }
53257                 }
53258                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53259             }
53260         }
53261         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53262         this.bind(null, null);
53263         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53264     },
53265
53266     handleLockChange : function(){
53267         this.refresh(true);
53268     },
53269
53270     onDenyColumnLock : function(){
53271
53272     },
53273
53274     onDenyColumnHide : function(){
53275
53276     },
53277
53278     handleHdMenuClick : function(item){
53279         var index = this.hdCtxIndex;
53280         var cm = this.cm, ds = this.ds;
53281         switch(item.id){
53282             case "asc":
53283                 ds.sort(cm.getDataIndex(index), "ASC");
53284                 break;
53285             case "desc":
53286                 ds.sort(cm.getDataIndex(index), "DESC");
53287                 break;
53288             case "lock":
53289                 var lc = cm.getLockedCount();
53290                 if(cm.getColumnCount(true) <= lc+1){
53291                     this.onDenyColumnLock();
53292                     return;
53293                 }
53294                 if(lc != index){
53295                     cm.setLocked(index, true, true);
53296                     cm.moveColumn(index, lc);
53297                     this.grid.fireEvent("columnmove", index, lc);
53298                 }else{
53299                     cm.setLocked(index, true);
53300                 }
53301             break;
53302             case "unlock":
53303                 var lc = cm.getLockedCount();
53304                 if((lc-1) != index){
53305                     cm.setLocked(index, false, true);
53306                     cm.moveColumn(index, lc-1);
53307                     this.grid.fireEvent("columnmove", index, lc-1);
53308                 }else{
53309                     cm.setLocked(index, false);
53310                 }
53311             break;
53312             default:
53313                 index = cm.getIndexById(item.id.substr(4));
53314                 if(index != -1){
53315                     if(item.checked && cm.getColumnCount(true) <= 1){
53316                         this.onDenyColumnHide();
53317                         return false;
53318                     }
53319                     cm.setHidden(index, item.checked);
53320                 }
53321         }
53322         return true;
53323     },
53324
53325     beforeColMenuShow : function(){
53326         var cm = this.cm,  colCount = cm.getColumnCount();
53327         this.colMenu.removeAll();
53328         for(var i = 0; i < colCount; i++){
53329             this.colMenu.add(new Roo.menu.CheckItem({
53330                 id: "col-"+cm.getColumnId(i),
53331                 text: cm.getColumnHeader(i),
53332                 checked: !cm.isHidden(i),
53333                 hideOnClick:false
53334             }));
53335         }
53336     },
53337
53338     handleHdCtx : function(g, index, e){
53339         e.stopEvent();
53340         var hd = this.getHeaderCell(index);
53341         this.hdCtxIndex = index;
53342         var ms = this.hmenu.items, cm = this.cm;
53343         ms.get("asc").setDisabled(!cm.isSortable(index));
53344         ms.get("desc").setDisabled(!cm.isSortable(index));
53345         if(this.grid.enableColLock !== false){
53346             ms.get("lock").setDisabled(cm.isLocked(index));
53347             ms.get("unlock").setDisabled(!cm.isLocked(index));
53348         }
53349         this.hmenu.show(hd, "tl-bl");
53350     },
53351
53352     handleHdOver : function(e){
53353         var hd = this.findHeaderCell(e.getTarget());
53354         if(hd && !this.headersDisabled){
53355             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53356                this.fly(hd).addClass("x-grid-hd-over");
53357             }
53358         }
53359     },
53360
53361     handleHdOut : function(e){
53362         var hd = this.findHeaderCell(e.getTarget());
53363         if(hd){
53364             this.fly(hd).removeClass("x-grid-hd-over");
53365         }
53366     },
53367
53368     handleSplitDblClick : function(e, t){
53369         var i = this.getCellIndex(t);
53370         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53371             this.autoSizeColumn(i, true);
53372             this.layout();
53373         }
53374     },
53375
53376     render : function(){
53377
53378         var cm = this.cm;
53379         var colCount = cm.getColumnCount();
53380
53381         if(this.grid.monitorWindowResize === true){
53382             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53383         }
53384         var header = this.renderHeaders();
53385         var body = this.templates.body.apply({rows:""});
53386         var html = this.templates.master.apply({
53387             lockedBody: body,
53388             body: body,
53389             lockedHeader: header[0],
53390             header: header[1]
53391         });
53392
53393         //this.updateColumns();
53394
53395         this.grid.getGridEl().dom.innerHTML = html;
53396
53397         this.initElements();
53398         
53399         // a kludge to fix the random scolling effect in webkit
53400         this.el.on("scroll", function() {
53401             this.el.dom.scrollTop=0; // hopefully not recursive..
53402         },this);
53403
53404         this.scroller.on("scroll", this.handleScroll, this);
53405         this.lockedBody.on("mousewheel", this.handleWheel, this);
53406         this.mainBody.on("mousewheel", this.handleWheel, this);
53407
53408         this.mainHd.on("mouseover", this.handleHdOver, this);
53409         this.mainHd.on("mouseout", this.handleHdOut, this);
53410         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53411                 {delegate: "."+this.splitClass});
53412
53413         this.lockedHd.on("mouseover", this.handleHdOver, this);
53414         this.lockedHd.on("mouseout", this.handleHdOut, this);
53415         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53416                 {delegate: "."+this.splitClass});
53417
53418         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53419             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53420         }
53421
53422         this.updateSplitters();
53423
53424         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53425             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53426             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53427         }
53428
53429         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53430             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53431             this.hmenu.add(
53432                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53433                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53434             );
53435             if(this.grid.enableColLock !== false){
53436                 this.hmenu.add('-',
53437                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53438                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53439                 );
53440             }
53441             if(this.grid.enableColumnHide !== false){
53442
53443                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53444                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53445                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53446
53447                 this.hmenu.add('-',
53448                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53449                 );
53450             }
53451             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53452
53453             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53454         }
53455
53456         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53457             this.dd = new Roo.grid.GridDragZone(this.grid, {
53458                 ddGroup : this.grid.ddGroup || 'GridDD'
53459             });
53460             
53461         }
53462
53463         /*
53464         for(var i = 0; i < colCount; i++){
53465             if(cm.isHidden(i)){
53466                 this.hideColumn(i);
53467             }
53468             if(cm.config[i].align){
53469                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53470                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53471             }
53472         }*/
53473         
53474         this.updateHeaderSortState();
53475
53476         this.beforeInitialResize();
53477         this.layout(true);
53478
53479         // two part rendering gives faster view to the user
53480         this.renderPhase2.defer(1, this);
53481     },
53482
53483     renderPhase2 : function(){
53484         // render the rows now
53485         this.refresh();
53486         if(this.grid.autoSizeColumns){
53487             this.autoSizeColumns();
53488         }
53489     },
53490
53491     beforeInitialResize : function(){
53492
53493     },
53494
53495     onColumnSplitterMoved : function(i, w){
53496         this.userResized = true;
53497         var cm = this.grid.colModel;
53498         cm.setColumnWidth(i, w, true);
53499         var cid = cm.getColumnId(i);
53500         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53501         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53502         this.updateSplitters();
53503         this.layout();
53504         this.grid.fireEvent("columnresize", i, w);
53505     },
53506
53507     syncRowHeights : function(startIndex, endIndex){
53508         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53509             startIndex = startIndex || 0;
53510             var mrows = this.getBodyTable().rows;
53511             var lrows = this.getLockedTable().rows;
53512             var len = mrows.length-1;
53513             endIndex = Math.min(endIndex || len, len);
53514             for(var i = startIndex; i <= endIndex; i++){
53515                 var m = mrows[i], l = lrows[i];
53516                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53517                 m.style.height = l.style.height = h + "px";
53518             }
53519         }
53520     },
53521
53522     layout : function(initialRender, is2ndPass){
53523         var g = this.grid;
53524         var auto = g.autoHeight;
53525         var scrollOffset = 16;
53526         var c = g.getGridEl(), cm = this.cm,
53527                 expandCol = g.autoExpandColumn,
53528                 gv = this;
53529         //c.beginMeasure();
53530
53531         if(!c.dom.offsetWidth){ // display:none?
53532             if(initialRender){
53533                 this.lockedWrap.show();
53534                 this.mainWrap.show();
53535             }
53536             return;
53537         }
53538
53539         var hasLock = this.cm.isLocked(0);
53540
53541         var tbh = this.headerPanel.getHeight();
53542         var bbh = this.footerPanel.getHeight();
53543
53544         if(auto){
53545             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53546             var newHeight = ch + c.getBorderWidth("tb");
53547             if(g.maxHeight){
53548                 newHeight = Math.min(g.maxHeight, newHeight);
53549             }
53550             c.setHeight(newHeight);
53551         }
53552
53553         if(g.autoWidth){
53554             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53555         }
53556
53557         var s = this.scroller;
53558
53559         var csize = c.getSize(true);
53560
53561         this.el.setSize(csize.width, csize.height);
53562
53563         this.headerPanel.setWidth(csize.width);
53564         this.footerPanel.setWidth(csize.width);
53565
53566         var hdHeight = this.mainHd.getHeight();
53567         var vw = csize.width;
53568         var vh = csize.height - (tbh + bbh);
53569
53570         s.setSize(vw, vh);
53571
53572         var bt = this.getBodyTable();
53573         var ltWidth = hasLock ?
53574                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53575
53576         var scrollHeight = bt.offsetHeight;
53577         var scrollWidth = ltWidth + bt.offsetWidth;
53578         var vscroll = false, hscroll = false;
53579
53580         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53581
53582         var lw = this.lockedWrap, mw = this.mainWrap;
53583         var lb = this.lockedBody, mb = this.mainBody;
53584
53585         setTimeout(function(){
53586             var t = s.dom.offsetTop;
53587             var w = s.dom.clientWidth,
53588                 h = s.dom.clientHeight;
53589
53590             lw.setTop(t);
53591             lw.setSize(ltWidth, h);
53592
53593             mw.setLeftTop(ltWidth, t);
53594             mw.setSize(w-ltWidth, h);
53595
53596             lb.setHeight(h-hdHeight);
53597             mb.setHeight(h-hdHeight);
53598
53599             if(is2ndPass !== true && !gv.userResized && expandCol){
53600                 // high speed resize without full column calculation
53601                 
53602                 var ci = cm.getIndexById(expandCol);
53603                 if (ci < 0) {
53604                     ci = cm.findColumnIndex(expandCol);
53605                 }
53606                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53607                 var expandId = cm.getColumnId(ci);
53608                 var  tw = cm.getTotalWidth(false);
53609                 var currentWidth = cm.getColumnWidth(ci);
53610                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53611                 if(currentWidth != cw){
53612                     cm.setColumnWidth(ci, cw, true);
53613                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53614                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53615                     gv.updateSplitters();
53616                     gv.layout(false, true);
53617                 }
53618             }
53619
53620             if(initialRender){
53621                 lw.show();
53622                 mw.show();
53623             }
53624             //c.endMeasure();
53625         }, 10);
53626     },
53627
53628     onWindowResize : function(){
53629         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53630             return;
53631         }
53632         this.layout();
53633     },
53634
53635     appendFooter : function(parentEl){
53636         return null;
53637     },
53638
53639     sortAscText : "Sort Ascending",
53640     sortDescText : "Sort Descending",
53641     lockText : "Lock Column",
53642     unlockText : "Unlock Column",
53643     columnsText : "Columns"
53644 });
53645
53646
53647 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53648     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53649     this.proxy.el.addClass('x-grid3-col-dd');
53650 };
53651
53652 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53653     handleMouseDown : function(e){
53654
53655     },
53656
53657     callHandleMouseDown : function(e){
53658         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53659     }
53660 });
53661 /*
53662  * Based on:
53663  * Ext JS Library 1.1.1
53664  * Copyright(c) 2006-2007, Ext JS, LLC.
53665  *
53666  * Originally Released Under LGPL - original licence link has changed is not relivant.
53667  *
53668  * Fork - LGPL
53669  * <script type="text/javascript">
53670  */
53671  
53672 // private
53673 // This is a support class used internally by the Grid components
53674 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53675     this.grid = grid;
53676     this.view = grid.getView();
53677     this.proxy = this.view.resizeProxy;
53678     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53679         "gridSplitters" + this.grid.getGridEl().id, {
53680         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53681     });
53682     this.setHandleElId(Roo.id(hd));
53683     this.setOuterHandleElId(Roo.id(hd2));
53684     this.scroll = false;
53685 };
53686 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53687     fly: Roo.Element.fly,
53688
53689     b4StartDrag : function(x, y){
53690         this.view.headersDisabled = true;
53691         this.proxy.setHeight(this.view.mainWrap.getHeight());
53692         var w = this.cm.getColumnWidth(this.cellIndex);
53693         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53694         this.resetConstraints();
53695         this.setXConstraint(minw, 1000);
53696         this.setYConstraint(0, 0);
53697         this.minX = x - minw;
53698         this.maxX = x + 1000;
53699         this.startPos = x;
53700         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53701     },
53702
53703
53704     handleMouseDown : function(e){
53705         ev = Roo.EventObject.setEvent(e);
53706         var t = this.fly(ev.getTarget());
53707         if(t.hasClass("x-grid-split")){
53708             this.cellIndex = this.view.getCellIndex(t.dom);
53709             this.split = t.dom;
53710             this.cm = this.grid.colModel;
53711             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53712                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53713             }
53714         }
53715     },
53716
53717     endDrag : function(e){
53718         this.view.headersDisabled = false;
53719         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53720         var diff = endX - this.startPos;
53721         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53722     },
53723
53724     autoOffset : function(){
53725         this.setDelta(0,0);
53726     }
53727 });/*
53728  * Based on:
53729  * Ext JS Library 1.1.1
53730  * Copyright(c) 2006-2007, Ext JS, LLC.
53731  *
53732  * Originally Released Under LGPL - original licence link has changed is not relivant.
53733  *
53734  * Fork - LGPL
53735  * <script type="text/javascript">
53736  */
53737  
53738 // private
53739 // This is a support class used internally by the Grid components
53740 Roo.grid.GridDragZone = function(grid, config){
53741     this.view = grid.getView();
53742     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53743     if(this.view.lockedBody){
53744         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53745         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53746     }
53747     this.scroll = false;
53748     this.grid = grid;
53749     this.ddel = document.createElement('div');
53750     this.ddel.className = 'x-grid-dd-wrap';
53751 };
53752
53753 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53754     ddGroup : "GridDD",
53755
53756     getDragData : function(e){
53757         var t = Roo.lib.Event.getTarget(e);
53758         var rowIndex = this.view.findRowIndex(t);
53759         var sm = this.grid.selModel;
53760             
53761         //Roo.log(rowIndex);
53762         
53763         if (sm.getSelectedCell) {
53764             // cell selection..
53765             if (!sm.getSelectedCell()) {
53766                 return false;
53767             }
53768             if (rowIndex != sm.getSelectedCell()[0]) {
53769                 return false;
53770             }
53771         
53772         }
53773         
53774         if(rowIndex !== false){
53775             
53776             // if editorgrid.. 
53777             
53778             
53779             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53780                
53781             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53782               //  
53783             //}
53784             if (e.hasModifier()){
53785                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53786             }
53787             
53788             Roo.log("getDragData");
53789             
53790             return {
53791                 grid: this.grid,
53792                 ddel: this.ddel,
53793                 rowIndex: rowIndex,
53794                 selections:sm.getSelections ? sm.getSelections() : (
53795                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53796                 )
53797             };
53798         }
53799         return false;
53800     },
53801
53802     onInitDrag : function(e){
53803         var data = this.dragData;
53804         this.ddel.innerHTML = this.grid.getDragDropText();
53805         this.proxy.update(this.ddel);
53806         // fire start drag?
53807     },
53808
53809     afterRepair : function(){
53810         this.dragging = false;
53811     },
53812
53813     getRepairXY : function(e, data){
53814         return false;
53815     },
53816
53817     onEndDrag : function(data, e){
53818         // fire end drag?
53819     },
53820
53821     onValidDrop : function(dd, e, id){
53822         // fire drag drop?
53823         this.hideProxy();
53824     },
53825
53826     beforeInvalidDrop : function(e, id){
53827
53828     }
53829 });/*
53830  * Based on:
53831  * Ext JS Library 1.1.1
53832  * Copyright(c) 2006-2007, Ext JS, LLC.
53833  *
53834  * Originally Released Under LGPL - original licence link has changed is not relivant.
53835  *
53836  * Fork - LGPL
53837  * <script type="text/javascript">
53838  */
53839  
53840
53841 /**
53842  * @class Roo.grid.ColumnModel
53843  * @extends Roo.util.Observable
53844  * This is the default implementation of a ColumnModel used by the Grid. It defines
53845  * the columns in the grid.
53846  * <br>Usage:<br>
53847  <pre><code>
53848  var colModel = new Roo.grid.ColumnModel([
53849         {header: "Ticker", width: 60, sortable: true, locked: true},
53850         {header: "Company Name", width: 150, sortable: true},
53851         {header: "Market Cap.", width: 100, sortable: true},
53852         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53853         {header: "Employees", width: 100, sortable: true, resizable: false}
53854  ]);
53855  </code></pre>
53856  * <p>
53857  
53858  * The config options listed for this class are options which may appear in each
53859  * individual column definition.
53860  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53861  * @constructor
53862  * @param {Object} config An Array of column config objects. See this class's
53863  * config objects for details.
53864 */
53865 Roo.grid.ColumnModel = function(config){
53866         /**
53867      * The config passed into the constructor
53868      */
53869     this.config = config;
53870     this.lookup = {};
53871
53872     // if no id, create one
53873     // if the column does not have a dataIndex mapping,
53874     // map it to the order it is in the config
53875     for(var i = 0, len = config.length; i < len; i++){
53876         var c = config[i];
53877         if(typeof c.dataIndex == "undefined"){
53878             c.dataIndex = i;
53879         }
53880         if(typeof c.renderer == "string"){
53881             c.renderer = Roo.util.Format[c.renderer];
53882         }
53883         if(typeof c.id == "undefined"){
53884             c.id = Roo.id();
53885         }
53886         if(c.editor && c.editor.xtype){
53887             c.editor  = Roo.factory(c.editor, Roo.grid);
53888         }
53889         if(c.editor && c.editor.isFormField){
53890             c.editor = new Roo.grid.GridEditor(c.editor);
53891         }
53892         this.lookup[c.id] = c;
53893     }
53894
53895     /**
53896      * The width of columns which have no width specified (defaults to 100)
53897      * @type Number
53898      */
53899     this.defaultWidth = 100;
53900
53901     /**
53902      * Default sortable of columns which have no sortable specified (defaults to false)
53903      * @type Boolean
53904      */
53905     this.defaultSortable = false;
53906
53907     this.addEvents({
53908         /**
53909              * @event widthchange
53910              * Fires when the width of a column changes.
53911              * @param {ColumnModel} this
53912              * @param {Number} columnIndex The column index
53913              * @param {Number} newWidth The new width
53914              */
53915             "widthchange": true,
53916         /**
53917              * @event headerchange
53918              * Fires when the text of a header changes.
53919              * @param {ColumnModel} this
53920              * @param {Number} columnIndex The column index
53921              * @param {Number} newText The new header text
53922              */
53923             "headerchange": true,
53924         /**
53925              * @event hiddenchange
53926              * Fires when a column is hidden or "unhidden".
53927              * @param {ColumnModel} this
53928              * @param {Number} columnIndex The column index
53929              * @param {Boolean} hidden true if hidden, false otherwise
53930              */
53931             "hiddenchange": true,
53932             /**
53933          * @event columnmoved
53934          * Fires when a column is moved.
53935          * @param {ColumnModel} this
53936          * @param {Number} oldIndex
53937          * @param {Number} newIndex
53938          */
53939         "columnmoved" : true,
53940         /**
53941          * @event columlockchange
53942          * Fires when a column's locked state is changed
53943          * @param {ColumnModel} this
53944          * @param {Number} colIndex
53945          * @param {Boolean} locked true if locked
53946          */
53947         "columnlockchange" : true
53948     });
53949     Roo.grid.ColumnModel.superclass.constructor.call(this);
53950 };
53951 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
53952     /**
53953      * @cfg {String} header The header text to display in the Grid view.
53954      */
53955     /**
53956      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
53957      * {@link Roo.data.Record} definition from which to draw the column's value. If not
53958      * specified, the column's index is used as an index into the Record's data Array.
53959      */
53960     /**
53961      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
53962      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
53963      */
53964     /**
53965      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
53966      * Defaults to the value of the {@link #defaultSortable} property.
53967      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
53968      */
53969     /**
53970      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
53971      */
53972     /**
53973      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
53974      */
53975     /**
53976      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
53977      */
53978     /**
53979      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
53980      */
53981     /**
53982      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
53983      * given the cell's data value. See {@link #setRenderer}. If not specified, the
53984      * default renderer uses the raw data value.
53985      */
53986        /**
53987      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
53988      */
53989     /**
53990      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
53991      */
53992
53993     /**
53994      * Returns the id of the column at the specified index.
53995      * @param {Number} index The column index
53996      * @return {String} the id
53997      */
53998     getColumnId : function(index){
53999         return this.config[index].id;
54000     },
54001
54002     /**
54003      * Returns the column for a specified id.
54004      * @param {String} id The column id
54005      * @return {Object} the column
54006      */
54007     getColumnById : function(id){
54008         return this.lookup[id];
54009     },
54010
54011     
54012     /**
54013      * Returns the column for a specified dataIndex.
54014      * @param {String} dataIndex The column dataIndex
54015      * @return {Object|Boolean} the column or false if not found
54016      */
54017     getColumnByDataIndex: function(dataIndex){
54018         var index = this.findColumnIndex(dataIndex);
54019         return index > -1 ? this.config[index] : false;
54020     },
54021     
54022     /**
54023      * Returns the index for a specified column id.
54024      * @param {String} id The column id
54025      * @return {Number} the index, or -1 if not found
54026      */
54027     getIndexById : function(id){
54028         for(var i = 0, len = this.config.length; i < len; i++){
54029             if(this.config[i].id == id){
54030                 return i;
54031             }
54032         }
54033         return -1;
54034     },
54035     
54036     /**
54037      * Returns the index for a specified column dataIndex.
54038      * @param {String} dataIndex The column dataIndex
54039      * @return {Number} the index, or -1 if not found
54040      */
54041     
54042     findColumnIndex : function(dataIndex){
54043         for(var i = 0, len = this.config.length; i < len; i++){
54044             if(this.config[i].dataIndex == dataIndex){
54045                 return i;
54046             }
54047         }
54048         return -1;
54049     },
54050     
54051     
54052     moveColumn : function(oldIndex, newIndex){
54053         var c = this.config[oldIndex];
54054         this.config.splice(oldIndex, 1);
54055         this.config.splice(newIndex, 0, c);
54056         this.dataMap = null;
54057         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54058     },
54059
54060     isLocked : function(colIndex){
54061         return this.config[colIndex].locked === true;
54062     },
54063
54064     setLocked : function(colIndex, value, suppressEvent){
54065         if(this.isLocked(colIndex) == value){
54066             return;
54067         }
54068         this.config[colIndex].locked = value;
54069         if(!suppressEvent){
54070             this.fireEvent("columnlockchange", this, colIndex, value);
54071         }
54072     },
54073
54074     getTotalLockedWidth : function(){
54075         var totalWidth = 0;
54076         for(var i = 0; i < this.config.length; i++){
54077             if(this.isLocked(i) && !this.isHidden(i)){
54078                 this.totalWidth += this.getColumnWidth(i);
54079             }
54080         }
54081         return totalWidth;
54082     },
54083
54084     getLockedCount : function(){
54085         for(var i = 0, len = this.config.length; i < len; i++){
54086             if(!this.isLocked(i)){
54087                 return i;
54088             }
54089         }
54090     },
54091
54092     /**
54093      * Returns the number of columns.
54094      * @return {Number}
54095      */
54096     getColumnCount : function(visibleOnly){
54097         if(visibleOnly === true){
54098             var c = 0;
54099             for(var i = 0, len = this.config.length; i < len; i++){
54100                 if(!this.isHidden(i)){
54101                     c++;
54102                 }
54103             }
54104             return c;
54105         }
54106         return this.config.length;
54107     },
54108
54109     /**
54110      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54111      * @param {Function} fn
54112      * @param {Object} scope (optional)
54113      * @return {Array} result
54114      */
54115     getColumnsBy : function(fn, scope){
54116         var r = [];
54117         for(var i = 0, len = this.config.length; i < len; i++){
54118             var c = this.config[i];
54119             if(fn.call(scope||this, c, i) === true){
54120                 r[r.length] = c;
54121             }
54122         }
54123         return r;
54124     },
54125
54126     /**
54127      * Returns true if the specified column is sortable.
54128      * @param {Number} col The column index
54129      * @return {Boolean}
54130      */
54131     isSortable : function(col){
54132         if(typeof this.config[col].sortable == "undefined"){
54133             return this.defaultSortable;
54134         }
54135         return this.config[col].sortable;
54136     },
54137
54138     /**
54139      * Returns the rendering (formatting) function defined for the column.
54140      * @param {Number} col The column index.
54141      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54142      */
54143     getRenderer : function(col){
54144         if(!this.config[col].renderer){
54145             return Roo.grid.ColumnModel.defaultRenderer;
54146         }
54147         return this.config[col].renderer;
54148     },
54149
54150     /**
54151      * Sets the rendering (formatting) function for a column.
54152      * @param {Number} col The column index
54153      * @param {Function} fn The function to use to process the cell's raw data
54154      * to return HTML markup for the grid view. The render function is called with
54155      * the following parameters:<ul>
54156      * <li>Data value.</li>
54157      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54158      * <li>css A CSS style string to apply to the table cell.</li>
54159      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54160      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54161      * <li>Row index</li>
54162      * <li>Column index</li>
54163      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54164      */
54165     setRenderer : function(col, fn){
54166         this.config[col].renderer = fn;
54167     },
54168
54169     /**
54170      * Returns the width for the specified column.
54171      * @param {Number} col The column index
54172      * @return {Number}
54173      */
54174     getColumnWidth : function(col){
54175         return this.config[col].width * 1 || this.defaultWidth;
54176     },
54177
54178     /**
54179      * Sets the width for a column.
54180      * @param {Number} col The column index
54181      * @param {Number} width The new width
54182      */
54183     setColumnWidth : function(col, width, suppressEvent){
54184         this.config[col].width = width;
54185         this.totalWidth = null;
54186         if(!suppressEvent){
54187              this.fireEvent("widthchange", this, col, width);
54188         }
54189     },
54190
54191     /**
54192      * Returns the total width of all columns.
54193      * @param {Boolean} includeHidden True to include hidden column widths
54194      * @return {Number}
54195      */
54196     getTotalWidth : function(includeHidden){
54197         if(!this.totalWidth){
54198             this.totalWidth = 0;
54199             for(var i = 0, len = this.config.length; i < len; i++){
54200                 if(includeHidden || !this.isHidden(i)){
54201                     this.totalWidth += this.getColumnWidth(i);
54202                 }
54203             }
54204         }
54205         return this.totalWidth;
54206     },
54207
54208     /**
54209      * Returns the header for the specified column.
54210      * @param {Number} col The column index
54211      * @return {String}
54212      */
54213     getColumnHeader : function(col){
54214         return this.config[col].header;
54215     },
54216
54217     /**
54218      * Sets the header for a column.
54219      * @param {Number} col The column index
54220      * @param {String} header The new header
54221      */
54222     setColumnHeader : function(col, header){
54223         this.config[col].header = header;
54224         this.fireEvent("headerchange", this, col, header);
54225     },
54226
54227     /**
54228      * Returns the tooltip for the specified column.
54229      * @param {Number} col The column index
54230      * @return {String}
54231      */
54232     getColumnTooltip : function(col){
54233             return this.config[col].tooltip;
54234     },
54235     /**
54236      * Sets the tooltip for a column.
54237      * @param {Number} col The column index
54238      * @param {String} tooltip The new tooltip
54239      */
54240     setColumnTooltip : function(col, tooltip){
54241             this.config[col].tooltip = tooltip;
54242     },
54243
54244     /**
54245      * Returns the dataIndex for the specified column.
54246      * @param {Number} col The column index
54247      * @return {Number}
54248      */
54249     getDataIndex : function(col){
54250         return this.config[col].dataIndex;
54251     },
54252
54253     /**
54254      * Sets the dataIndex for a column.
54255      * @param {Number} col The column index
54256      * @param {Number} dataIndex The new dataIndex
54257      */
54258     setDataIndex : function(col, dataIndex){
54259         this.config[col].dataIndex = dataIndex;
54260     },
54261
54262     
54263     
54264     /**
54265      * Returns true if the cell is editable.
54266      * @param {Number} colIndex The column index
54267      * @param {Number} rowIndex The row index
54268      * @return {Boolean}
54269      */
54270     isCellEditable : function(colIndex, rowIndex){
54271         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54272     },
54273
54274     /**
54275      * Returns the editor defined for the cell/column.
54276      * return false or null to disable editing.
54277      * @param {Number} colIndex The column index
54278      * @param {Number} rowIndex The row index
54279      * @return {Object}
54280      */
54281     getCellEditor : function(colIndex, rowIndex){
54282         return this.config[colIndex].editor;
54283     },
54284
54285     /**
54286      * Sets if a column is editable.
54287      * @param {Number} col The column index
54288      * @param {Boolean} editable True if the column is editable
54289      */
54290     setEditable : function(col, editable){
54291         this.config[col].editable = editable;
54292     },
54293
54294
54295     /**
54296      * Returns true if the column is hidden.
54297      * @param {Number} colIndex The column index
54298      * @return {Boolean}
54299      */
54300     isHidden : function(colIndex){
54301         return this.config[colIndex].hidden;
54302     },
54303
54304
54305     /**
54306      * Returns true if the column width cannot be changed
54307      */
54308     isFixed : function(colIndex){
54309         return this.config[colIndex].fixed;
54310     },
54311
54312     /**
54313      * Returns true if the column can be resized
54314      * @return {Boolean}
54315      */
54316     isResizable : function(colIndex){
54317         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54318     },
54319     /**
54320      * Sets if a column is hidden.
54321      * @param {Number} colIndex The column index
54322      * @param {Boolean} hidden True if the column is hidden
54323      */
54324     setHidden : function(colIndex, hidden){
54325         this.config[colIndex].hidden = hidden;
54326         this.totalWidth = null;
54327         this.fireEvent("hiddenchange", this, colIndex, hidden);
54328     },
54329
54330     /**
54331      * Sets the editor for a column.
54332      * @param {Number} col The column index
54333      * @param {Object} editor The editor object
54334      */
54335     setEditor : function(col, editor){
54336         this.config[col].editor = editor;
54337     }
54338 });
54339
54340 Roo.grid.ColumnModel.defaultRenderer = function(value){
54341         if(typeof value == "string" && value.length < 1){
54342             return "&#160;";
54343         }
54344         return value;
54345 };
54346
54347 // Alias for backwards compatibility
54348 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54349 /*
54350  * Based on:
54351  * Ext JS Library 1.1.1
54352  * Copyright(c) 2006-2007, Ext JS, LLC.
54353  *
54354  * Originally Released Under LGPL - original licence link has changed is not relivant.
54355  *
54356  * Fork - LGPL
54357  * <script type="text/javascript">
54358  */
54359
54360 /**
54361  * @class Roo.grid.AbstractSelectionModel
54362  * @extends Roo.util.Observable
54363  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54364  * implemented by descendant classes.  This class should not be directly instantiated.
54365  * @constructor
54366  */
54367 Roo.grid.AbstractSelectionModel = function(){
54368     this.locked = false;
54369     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54370 };
54371
54372 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54373     /** @ignore Called by the grid automatically. Do not call directly. */
54374     init : function(grid){
54375         this.grid = grid;
54376         this.initEvents();
54377     },
54378
54379     /**
54380      * Locks the selections.
54381      */
54382     lock : function(){
54383         this.locked = true;
54384     },
54385
54386     /**
54387      * Unlocks the selections.
54388      */
54389     unlock : function(){
54390         this.locked = false;
54391     },
54392
54393     /**
54394      * Returns true if the selections are locked.
54395      * @return {Boolean}
54396      */
54397     isLocked : function(){
54398         return this.locked;
54399     }
54400 });/*
54401  * Based on:
54402  * Ext JS Library 1.1.1
54403  * Copyright(c) 2006-2007, Ext JS, LLC.
54404  *
54405  * Originally Released Under LGPL - original licence link has changed is not relivant.
54406  *
54407  * Fork - LGPL
54408  * <script type="text/javascript">
54409  */
54410 /**
54411  * @extends Roo.grid.AbstractSelectionModel
54412  * @class Roo.grid.RowSelectionModel
54413  * The default SelectionModel used by {@link Roo.grid.Grid}.
54414  * It supports multiple selections and keyboard selection/navigation. 
54415  * @constructor
54416  * @param {Object} config
54417  */
54418 Roo.grid.RowSelectionModel = function(config){
54419     Roo.apply(this, config);
54420     this.selections = new Roo.util.MixedCollection(false, function(o){
54421         return o.id;
54422     });
54423
54424     this.last = false;
54425     this.lastActive = false;
54426
54427     this.addEvents({
54428         /**
54429              * @event selectionchange
54430              * Fires when the selection changes
54431              * @param {SelectionModel} this
54432              */
54433             "selectionchange" : true,
54434         /**
54435              * @event afterselectionchange
54436              * Fires after the selection changes (eg. by key press or clicking)
54437              * @param {SelectionModel} this
54438              */
54439             "afterselectionchange" : true,
54440         /**
54441              * @event beforerowselect
54442              * Fires when a row is selected being selected, return false to cancel.
54443              * @param {SelectionModel} this
54444              * @param {Number} rowIndex The selected index
54445              * @param {Boolean} keepExisting False if other selections will be cleared
54446              */
54447             "beforerowselect" : true,
54448         /**
54449              * @event rowselect
54450              * Fires when a row is selected.
54451              * @param {SelectionModel} this
54452              * @param {Number} rowIndex The selected index
54453              * @param {Roo.data.Record} r The record
54454              */
54455             "rowselect" : true,
54456         /**
54457              * @event rowdeselect
54458              * Fires when a row is deselected.
54459              * @param {SelectionModel} this
54460              * @param {Number} rowIndex The selected index
54461              */
54462         "rowdeselect" : true
54463     });
54464     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54465     this.locked = false;
54466 };
54467
54468 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54469     /**
54470      * @cfg {Boolean} singleSelect
54471      * True to allow selection of only one row at a time (defaults to false)
54472      */
54473     singleSelect : false,
54474
54475     // private
54476     initEvents : function(){
54477
54478         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54479             this.grid.on("mousedown", this.handleMouseDown, this);
54480         }else{ // allow click to work like normal
54481             this.grid.on("rowclick", this.handleDragableRowClick, this);
54482         }
54483
54484         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54485             "up" : function(e){
54486                 if(!e.shiftKey){
54487                     this.selectPrevious(e.shiftKey);
54488                 }else if(this.last !== false && this.lastActive !== false){
54489                     var last = this.last;
54490                     this.selectRange(this.last,  this.lastActive-1);
54491                     this.grid.getView().focusRow(this.lastActive);
54492                     if(last !== false){
54493                         this.last = last;
54494                     }
54495                 }else{
54496                     this.selectFirstRow();
54497                 }
54498                 this.fireEvent("afterselectionchange", this);
54499             },
54500             "down" : function(e){
54501                 if(!e.shiftKey){
54502                     this.selectNext(e.shiftKey);
54503                 }else if(this.last !== false && this.lastActive !== false){
54504                     var last = this.last;
54505                     this.selectRange(this.last,  this.lastActive+1);
54506                     this.grid.getView().focusRow(this.lastActive);
54507                     if(last !== false){
54508                         this.last = last;
54509                     }
54510                 }else{
54511                     this.selectFirstRow();
54512                 }
54513                 this.fireEvent("afterselectionchange", this);
54514             },
54515             scope: this
54516         });
54517
54518         var view = this.grid.view;
54519         view.on("refresh", this.onRefresh, this);
54520         view.on("rowupdated", this.onRowUpdated, this);
54521         view.on("rowremoved", this.onRemove, this);
54522     },
54523
54524     // private
54525     onRefresh : function(){
54526         var ds = this.grid.dataSource, i, v = this.grid.view;
54527         var s = this.selections;
54528         s.each(function(r){
54529             if((i = ds.indexOfId(r.id)) != -1){
54530                 v.onRowSelect(i);
54531             }else{
54532                 s.remove(r);
54533             }
54534         });
54535     },
54536
54537     // private
54538     onRemove : function(v, index, r){
54539         this.selections.remove(r);
54540     },
54541
54542     // private
54543     onRowUpdated : function(v, index, r){
54544         if(this.isSelected(r)){
54545             v.onRowSelect(index);
54546         }
54547     },
54548
54549     /**
54550      * Select records.
54551      * @param {Array} records The records to select
54552      * @param {Boolean} keepExisting (optional) True to keep existing selections
54553      */
54554     selectRecords : function(records, keepExisting){
54555         if(!keepExisting){
54556             this.clearSelections();
54557         }
54558         var ds = this.grid.dataSource;
54559         for(var i = 0, len = records.length; i < len; i++){
54560             this.selectRow(ds.indexOf(records[i]), true);
54561         }
54562     },
54563
54564     /**
54565      * Gets the number of selected rows.
54566      * @return {Number}
54567      */
54568     getCount : function(){
54569         return this.selections.length;
54570     },
54571
54572     /**
54573      * Selects the first row in the grid.
54574      */
54575     selectFirstRow : function(){
54576         this.selectRow(0);
54577     },
54578
54579     /**
54580      * Select the last row.
54581      * @param {Boolean} keepExisting (optional) True to keep existing selections
54582      */
54583     selectLastRow : function(keepExisting){
54584         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54585     },
54586
54587     /**
54588      * Selects the row immediately following the last selected row.
54589      * @param {Boolean} keepExisting (optional) True to keep existing selections
54590      */
54591     selectNext : function(keepExisting){
54592         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54593             this.selectRow(this.last+1, keepExisting);
54594             this.grid.getView().focusRow(this.last);
54595         }
54596     },
54597
54598     /**
54599      * Selects the row that precedes the last selected row.
54600      * @param {Boolean} keepExisting (optional) True to keep existing selections
54601      */
54602     selectPrevious : function(keepExisting){
54603         if(this.last){
54604             this.selectRow(this.last-1, keepExisting);
54605             this.grid.getView().focusRow(this.last);
54606         }
54607     },
54608
54609     /**
54610      * Returns the selected records
54611      * @return {Array} Array of selected records
54612      */
54613     getSelections : function(){
54614         return [].concat(this.selections.items);
54615     },
54616
54617     /**
54618      * Returns the first selected record.
54619      * @return {Record}
54620      */
54621     getSelected : function(){
54622         return this.selections.itemAt(0);
54623     },
54624
54625
54626     /**
54627      * Clears all selections.
54628      */
54629     clearSelections : function(fast){
54630         if(this.locked) return;
54631         if(fast !== true){
54632             var ds = this.grid.dataSource;
54633             var s = this.selections;
54634             s.each(function(r){
54635                 this.deselectRow(ds.indexOfId(r.id));
54636             }, this);
54637             s.clear();
54638         }else{
54639             this.selections.clear();
54640         }
54641         this.last = false;
54642     },
54643
54644
54645     /**
54646      * Selects all rows.
54647      */
54648     selectAll : function(){
54649         if(this.locked) return;
54650         this.selections.clear();
54651         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54652             this.selectRow(i, true);
54653         }
54654     },
54655
54656     /**
54657      * Returns True if there is a selection.
54658      * @return {Boolean}
54659      */
54660     hasSelection : function(){
54661         return this.selections.length > 0;
54662     },
54663
54664     /**
54665      * Returns True if the specified row is selected.
54666      * @param {Number/Record} record The record or index of the record to check
54667      * @return {Boolean}
54668      */
54669     isSelected : function(index){
54670         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54671         return (r && this.selections.key(r.id) ? true : false);
54672     },
54673
54674     /**
54675      * Returns True if the specified record id is selected.
54676      * @param {String} id The id of record to check
54677      * @return {Boolean}
54678      */
54679     isIdSelected : function(id){
54680         return (this.selections.key(id) ? true : false);
54681     },
54682
54683     // private
54684     handleMouseDown : function(e, t){
54685         var view = this.grid.getView(), rowIndex;
54686         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54687             return;
54688         };
54689         if(e.shiftKey && this.last !== false){
54690             var last = this.last;
54691             this.selectRange(last, rowIndex, e.ctrlKey);
54692             this.last = last; // reset the last
54693             view.focusRow(rowIndex);
54694         }else{
54695             var isSelected = this.isSelected(rowIndex);
54696             if(e.button !== 0 && isSelected){
54697                 view.focusRow(rowIndex);
54698             }else if(e.ctrlKey && isSelected){
54699                 this.deselectRow(rowIndex);
54700             }else if(!isSelected){
54701                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54702                 view.focusRow(rowIndex);
54703             }
54704         }
54705         this.fireEvent("afterselectionchange", this);
54706     },
54707     // private
54708     handleDragableRowClick :  function(grid, rowIndex, e) 
54709     {
54710         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54711             this.selectRow(rowIndex, false);
54712             grid.view.focusRow(rowIndex);
54713              this.fireEvent("afterselectionchange", this);
54714         }
54715     },
54716     
54717     /**
54718      * Selects multiple rows.
54719      * @param {Array} rows Array of the indexes of the row to select
54720      * @param {Boolean} keepExisting (optional) True to keep existing selections
54721      */
54722     selectRows : function(rows, keepExisting){
54723         if(!keepExisting){
54724             this.clearSelections();
54725         }
54726         for(var i = 0, len = rows.length; i < len; i++){
54727             this.selectRow(rows[i], true);
54728         }
54729     },
54730
54731     /**
54732      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54733      * @param {Number} startRow The index of the first row in the range
54734      * @param {Number} endRow The index of the last row in the range
54735      * @param {Boolean} keepExisting (optional) True to retain existing selections
54736      */
54737     selectRange : function(startRow, endRow, keepExisting){
54738         if(this.locked) return;
54739         if(!keepExisting){
54740             this.clearSelections();
54741         }
54742         if(startRow <= endRow){
54743             for(var i = startRow; i <= endRow; i++){
54744                 this.selectRow(i, true);
54745             }
54746         }else{
54747             for(var i = startRow; i >= endRow; i--){
54748                 this.selectRow(i, true);
54749             }
54750         }
54751     },
54752
54753     /**
54754      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54755      * @param {Number} startRow The index of the first row in the range
54756      * @param {Number} endRow The index of the last row in the range
54757      */
54758     deselectRange : function(startRow, endRow, preventViewNotify){
54759         if(this.locked) return;
54760         for(var i = startRow; i <= endRow; i++){
54761             this.deselectRow(i, preventViewNotify);
54762         }
54763     },
54764
54765     /**
54766      * Selects a row.
54767      * @param {Number} row The index of the row to select
54768      * @param {Boolean} keepExisting (optional) True to keep existing selections
54769      */
54770     selectRow : function(index, keepExisting, preventViewNotify){
54771         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54772         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54773             if(!keepExisting || this.singleSelect){
54774                 this.clearSelections();
54775             }
54776             var r = this.grid.dataSource.getAt(index);
54777             this.selections.add(r);
54778             this.last = this.lastActive = index;
54779             if(!preventViewNotify){
54780                 this.grid.getView().onRowSelect(index);
54781             }
54782             this.fireEvent("rowselect", this, index, r);
54783             this.fireEvent("selectionchange", this);
54784         }
54785     },
54786
54787     /**
54788      * Deselects a row.
54789      * @param {Number} row The index of the row to deselect
54790      */
54791     deselectRow : function(index, preventViewNotify){
54792         if(this.locked) return;
54793         if(this.last == index){
54794             this.last = false;
54795         }
54796         if(this.lastActive == index){
54797             this.lastActive = false;
54798         }
54799         var r = this.grid.dataSource.getAt(index);
54800         this.selections.remove(r);
54801         if(!preventViewNotify){
54802             this.grid.getView().onRowDeselect(index);
54803         }
54804         this.fireEvent("rowdeselect", this, index);
54805         this.fireEvent("selectionchange", this);
54806     },
54807
54808     // private
54809     restoreLast : function(){
54810         if(this._last){
54811             this.last = this._last;
54812         }
54813     },
54814
54815     // private
54816     acceptsNav : function(row, col, cm){
54817         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54818     },
54819
54820     // private
54821     onEditorKey : function(field, e){
54822         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54823         if(k == e.TAB){
54824             e.stopEvent();
54825             ed.completeEdit();
54826             if(e.shiftKey){
54827                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54828             }else{
54829                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54830             }
54831         }else if(k == e.ENTER && !e.ctrlKey){
54832             e.stopEvent();
54833             ed.completeEdit();
54834             if(e.shiftKey){
54835                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54836             }else{
54837                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54838             }
54839         }else if(k == e.ESC){
54840             ed.cancelEdit();
54841         }
54842         if(newCell){
54843             g.startEditing(newCell[0], newCell[1]);
54844         }
54845     }
54846 });/*
54847  * Based on:
54848  * Ext JS Library 1.1.1
54849  * Copyright(c) 2006-2007, Ext JS, LLC.
54850  *
54851  * Originally Released Under LGPL - original licence link has changed is not relivant.
54852  *
54853  * Fork - LGPL
54854  * <script type="text/javascript">
54855  */
54856 /**
54857  * @class Roo.grid.CellSelectionModel
54858  * @extends Roo.grid.AbstractSelectionModel
54859  * This class provides the basic implementation for cell selection in a grid.
54860  * @constructor
54861  * @param {Object} config The object containing the configuration of this model.
54862  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54863  */
54864 Roo.grid.CellSelectionModel = function(config){
54865     Roo.apply(this, config);
54866
54867     this.selection = null;
54868
54869     this.addEvents({
54870         /**
54871              * @event beforerowselect
54872              * Fires before a cell is selected.
54873              * @param {SelectionModel} this
54874              * @param {Number} rowIndex The selected row index
54875              * @param {Number} colIndex The selected cell index
54876              */
54877             "beforecellselect" : true,
54878         /**
54879              * @event cellselect
54880              * Fires when a cell is selected.
54881              * @param {SelectionModel} this
54882              * @param {Number} rowIndex The selected row index
54883              * @param {Number} colIndex The selected cell index
54884              */
54885             "cellselect" : true,
54886         /**
54887              * @event selectionchange
54888              * Fires when the active selection changes.
54889              * @param {SelectionModel} this
54890              * @param {Object} selection null for no selection or an object (o) with two properties
54891                 <ul>
54892                 <li>o.record: the record object for the row the selection is in</li>
54893                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54894                 </ul>
54895              */
54896             "selectionchange" : true,
54897         /**
54898              * @event tabend
54899              * Fires when the tab (or enter) was pressed on the last editable cell
54900              * You can use this to trigger add new row.
54901              * @param {SelectionModel} this
54902              */
54903             "tabend" : true,
54904          /**
54905              * @event beforeeditnext
54906              * Fires before the next editable sell is made active
54907              * You can use this to skip to another cell or fire the tabend
54908              *    if you set cell to false
54909              * @param {Object} eventdata object : { cell : [ row, col ] } 
54910              */
54911             "beforeeditnext" : true
54912     });
54913     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54914 };
54915
54916 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54917     
54918     enter_is_tab: false,
54919
54920     /** @ignore */
54921     initEvents : function(){
54922         this.grid.on("mousedown", this.handleMouseDown, this);
54923         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54924         var view = this.grid.view;
54925         view.on("refresh", this.onViewChange, this);
54926         view.on("rowupdated", this.onRowUpdated, this);
54927         view.on("beforerowremoved", this.clearSelections, this);
54928         view.on("beforerowsinserted", this.clearSelections, this);
54929         if(this.grid.isEditor){
54930             this.grid.on("beforeedit", this.beforeEdit,  this);
54931         }
54932     },
54933
54934         //private
54935     beforeEdit : function(e){
54936         this.select(e.row, e.column, false, true, e.record);
54937     },
54938
54939         //private
54940     onRowUpdated : function(v, index, r){
54941         if(this.selection && this.selection.record == r){
54942             v.onCellSelect(index, this.selection.cell[1]);
54943         }
54944     },
54945
54946         //private
54947     onViewChange : function(){
54948         this.clearSelections(true);
54949     },
54950
54951         /**
54952          * Returns the currently selected cell,.
54953          * @return {Array} The selected cell (row, column) or null if none selected.
54954          */
54955     getSelectedCell : function(){
54956         return this.selection ? this.selection.cell : null;
54957     },
54958
54959     /**
54960      * Clears all selections.
54961      * @param {Boolean} true to prevent the gridview from being notified about the change.
54962      */
54963     clearSelections : function(preventNotify){
54964         var s = this.selection;
54965         if(s){
54966             if(preventNotify !== true){
54967                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
54968             }
54969             this.selection = null;
54970             this.fireEvent("selectionchange", this, null);
54971         }
54972     },
54973
54974     /**
54975      * Returns true if there is a selection.
54976      * @return {Boolean}
54977      */
54978     hasSelection : function(){
54979         return this.selection ? true : false;
54980     },
54981
54982     /** @ignore */
54983     handleMouseDown : function(e, t){
54984         var v = this.grid.getView();
54985         if(this.isLocked()){
54986             return;
54987         };
54988         var row = v.findRowIndex(t);
54989         var cell = v.findCellIndex(t);
54990         if(row !== false && cell !== false){
54991             this.select(row, cell);
54992         }
54993     },
54994
54995     /**
54996      * Selects a cell.
54997      * @param {Number} rowIndex
54998      * @param {Number} collIndex
54999      */
55000     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55001         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55002             this.clearSelections();
55003             r = r || this.grid.dataSource.getAt(rowIndex);
55004             this.selection = {
55005                 record : r,
55006                 cell : [rowIndex, colIndex]
55007             };
55008             if(!preventViewNotify){
55009                 var v = this.grid.getView();
55010                 v.onCellSelect(rowIndex, colIndex);
55011                 if(preventFocus !== true){
55012                     v.focusCell(rowIndex, colIndex);
55013                 }
55014             }
55015             this.fireEvent("cellselect", this, rowIndex, colIndex);
55016             this.fireEvent("selectionchange", this, this.selection);
55017         }
55018     },
55019
55020         //private
55021     isSelectable : function(rowIndex, colIndex, cm){
55022         return !cm.isHidden(colIndex);
55023     },
55024
55025     /** @ignore */
55026     handleKeyDown : function(e){
55027         //Roo.log('Cell Sel Model handleKeyDown');
55028         if(!e.isNavKeyPress()){
55029             return;
55030         }
55031         var g = this.grid, s = this.selection;
55032         if(!s){
55033             e.stopEvent();
55034             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55035             if(cell){
55036                 this.select(cell[0], cell[1]);
55037             }
55038             return;
55039         }
55040         var sm = this;
55041         var walk = function(row, col, step){
55042             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55043         };
55044         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55045         var newCell;
55046
55047       
55048
55049         switch(k){
55050             case e.TAB:
55051                 // handled by onEditorKey
55052                 if (g.isEditor && g.editing) {
55053                     return;
55054                 }
55055                 if(e.shiftKey) {
55056                     newCell = walk(r, c-1, -1);
55057                 } else {
55058                     newCell = walk(r, c+1, 1);
55059                 }
55060                 break;
55061             
55062             case e.DOWN:
55063                newCell = walk(r+1, c, 1);
55064                 break;
55065             
55066             case e.UP:
55067                 newCell = walk(r-1, c, -1);
55068                 break;
55069             
55070             case e.RIGHT:
55071                 newCell = walk(r, c+1, 1);
55072                 break;
55073             
55074             case e.LEFT:
55075                 newCell = walk(r, c-1, -1);
55076                 break;
55077             
55078             case e.ENTER:
55079                 
55080                 if(g.isEditor && !g.editing){
55081                    g.startEditing(r, c);
55082                    e.stopEvent();
55083                    return;
55084                 }
55085                 
55086                 
55087              break;
55088         };
55089         if(newCell){
55090             this.select(newCell[0], newCell[1]);
55091             e.stopEvent();
55092             
55093         }
55094     },
55095
55096     acceptsNav : function(row, col, cm){
55097         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55098     },
55099     /**
55100      * Selects a cell.
55101      * @param {Number} field (not used) - as it's normally used as a listener
55102      * @param {Number} e - event - fake it by using
55103      *
55104      * var e = Roo.EventObjectImpl.prototype;
55105      * e.keyCode = e.TAB
55106      *
55107      * 
55108      */
55109     onEditorKey : function(field, e){
55110         
55111         var k = e.getKey(),
55112             newCell,
55113             g = this.grid,
55114             ed = g.activeEditor,
55115             forward = false;
55116         ///Roo.log('onEditorKey' + k);
55117         
55118         
55119         if (this.enter_is_tab && k == e.ENTER) {
55120             k = e.TAB;
55121         }
55122         
55123         if(k == e.TAB){
55124             if(e.shiftKey){
55125                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55126             }else{
55127                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55128                 forward = true;
55129             }
55130             
55131             e.stopEvent();
55132             
55133         } else if(k == e.ENTER &&  !e.ctrlKey){
55134             ed.completeEdit();
55135             e.stopEvent();
55136             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55137         
55138                 } else if(k == e.ESC){
55139             ed.cancelEdit();
55140         }
55141                 
55142         if (newCell) {
55143             var ecall = { cell : newCell, forward : forward };
55144             this.fireEvent('beforeeditnext', ecall );
55145             newCell = ecall.cell;
55146                         forward = ecall.forward;
55147         }
55148                 
55149         if(newCell){
55150             //Roo.log('next cell after edit');
55151             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55152         } else if (forward) {
55153             // tabbed past last
55154             this.fireEvent.defer(100, this, ['tabend',this]);
55155         }
55156     }
55157 });/*
55158  * Based on:
55159  * Ext JS Library 1.1.1
55160  * Copyright(c) 2006-2007, Ext JS, LLC.
55161  *
55162  * Originally Released Under LGPL - original licence link has changed is not relivant.
55163  *
55164  * Fork - LGPL
55165  * <script type="text/javascript">
55166  */
55167  
55168 /**
55169  * @class Roo.grid.EditorGrid
55170  * @extends Roo.grid.Grid
55171  * Class for creating and editable grid.
55172  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55173  * The container MUST have some type of size defined for the grid to fill. The container will be 
55174  * automatically set to position relative if it isn't already.
55175  * @param {Object} dataSource The data model to bind to
55176  * @param {Object} colModel The column model with info about this grid's columns
55177  */
55178 Roo.grid.EditorGrid = function(container, config){
55179     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55180     this.getGridEl().addClass("xedit-grid");
55181
55182     if(!this.selModel){
55183         this.selModel = new Roo.grid.CellSelectionModel();
55184     }
55185
55186     this.activeEditor = null;
55187
55188         this.addEvents({
55189             /**
55190              * @event beforeedit
55191              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55192              * <ul style="padding:5px;padding-left:16px;">
55193              * <li>grid - This grid</li>
55194              * <li>record - The record being edited</li>
55195              * <li>field - The field name being edited</li>
55196              * <li>value - The value for the field being edited.</li>
55197              * <li>row - The grid row index</li>
55198              * <li>column - The grid column index</li>
55199              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55200              * </ul>
55201              * @param {Object} e An edit event (see above for description)
55202              */
55203             "beforeedit" : true,
55204             /**
55205              * @event afteredit
55206              * Fires after a cell is edited. <br />
55207              * <ul style="padding:5px;padding-left:16px;">
55208              * <li>grid - This grid</li>
55209              * <li>record - The record being edited</li>
55210              * <li>field - The field name being edited</li>
55211              * <li>value - The value being set</li>
55212              * <li>originalValue - The original value for the field, before the edit.</li>
55213              * <li>row - The grid row index</li>
55214              * <li>column - The grid column index</li>
55215              * </ul>
55216              * @param {Object} e An edit event (see above for description)
55217              */
55218             "afteredit" : true,
55219             /**
55220              * @event validateedit
55221              * Fires after a cell is edited, but before the value is set in the record. 
55222          * You can use this to modify the value being set in the field, Return false
55223              * to cancel the change. The edit event object has the following properties <br />
55224              * <ul style="padding:5px;padding-left:16px;">
55225          * <li>editor - This editor</li>
55226              * <li>grid - This grid</li>
55227              * <li>record - The record being edited</li>
55228              * <li>field - The field name being edited</li>
55229              * <li>value - The value being set</li>
55230              * <li>originalValue - The original value for the field, before the edit.</li>
55231              * <li>row - The grid row index</li>
55232              * <li>column - The grid column index</li>
55233              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55234              * </ul>
55235              * @param {Object} e An edit event (see above for description)
55236              */
55237             "validateedit" : true
55238         });
55239     this.on("bodyscroll", this.stopEditing,  this);
55240     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55241 };
55242
55243 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55244     /**
55245      * @cfg {Number} clicksToEdit
55246      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55247      */
55248     clicksToEdit: 2,
55249
55250     // private
55251     isEditor : true,
55252     // private
55253     trackMouseOver: false, // causes very odd FF errors
55254
55255     onCellDblClick : function(g, row, col){
55256         this.startEditing(row, col);
55257     },
55258
55259     onEditComplete : function(ed, value, startValue){
55260         this.editing = false;
55261         this.activeEditor = null;
55262         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55263         var r = ed.record;
55264         var field = this.colModel.getDataIndex(ed.col);
55265         var e = {
55266             grid: this,
55267             record: r,
55268             field: field,
55269             originalValue: startValue,
55270             value: value,
55271             row: ed.row,
55272             column: ed.col,
55273             cancel:false,
55274             editor: ed
55275         };
55276         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55277         cell.show();
55278           
55279         if(String(value) !== String(startValue)){
55280             
55281             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55282                 r.set(field, e.value);
55283                 // if we are dealing with a combo box..
55284                 // then we also set the 'name' colum to be the displayField
55285                 if (ed.field.displayField && ed.field.name) {
55286                     r.set(ed.field.name, ed.field.el.dom.value);
55287                 }
55288                 
55289                 delete e.cancel; //?? why!!!
55290                 this.fireEvent("afteredit", e);
55291             }
55292         } else {
55293             this.fireEvent("afteredit", e); // always fire it!
55294         }
55295         this.view.focusCell(ed.row, ed.col);
55296     },
55297
55298     /**
55299      * Starts editing the specified for the specified row/column
55300      * @param {Number} rowIndex
55301      * @param {Number} colIndex
55302      */
55303     startEditing : function(row, col){
55304         this.stopEditing();
55305         if(this.colModel.isCellEditable(col, row)){
55306             this.view.ensureVisible(row, col, true);
55307           
55308             var r = this.dataSource.getAt(row);
55309             var field = this.colModel.getDataIndex(col);
55310             var cell = Roo.get(this.view.getCell(row,col));
55311             var e = {
55312                 grid: this,
55313                 record: r,
55314                 field: field,
55315                 value: r.data[field],
55316                 row: row,
55317                 column: col,
55318                 cancel:false 
55319             };
55320             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55321                 this.editing = true;
55322                 var ed = this.colModel.getCellEditor(col, row);
55323                 
55324                 if (!ed) {
55325                     return;
55326                 }
55327                 if(!ed.rendered){
55328                     ed.render(ed.parentEl || document.body);
55329                 }
55330                 ed.field.reset();
55331                
55332                 cell.hide();
55333                 
55334                 (function(){ // complex but required for focus issues in safari, ie and opera
55335                     ed.row = row;
55336                     ed.col = col;
55337                     ed.record = r;
55338                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55339                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55340                     this.activeEditor = ed;
55341                     var v = r.data[field];
55342                     ed.startEdit(this.view.getCell(row, col), v);
55343                     // combo's with 'displayField and name set
55344                     if (ed.field.displayField && ed.field.name) {
55345                         ed.field.el.dom.value = r.data[ed.field.name];
55346                     }
55347                     
55348                     
55349                 }).defer(50, this);
55350             }
55351         }
55352     },
55353         
55354     /**
55355      * Stops any active editing
55356      */
55357     stopEditing : function(){
55358         if(this.activeEditor){
55359             this.activeEditor.completeEdit();
55360         }
55361         this.activeEditor = null;
55362     },
55363         
55364          /**
55365      * Called to get grid's drag proxy text, by default returns this.ddText.
55366      * @return {String}
55367      */
55368     getDragDropText : function(){
55369         var count = this.selModel.getSelectedCell() ? 1 : 0;
55370         return String.format(this.ddText, count, count == 1 ? '' : 's');
55371     }
55372         
55373 });/*
55374  * Based on:
55375  * Ext JS Library 1.1.1
55376  * Copyright(c) 2006-2007, Ext JS, LLC.
55377  *
55378  * Originally Released Under LGPL - original licence link has changed is not relivant.
55379  *
55380  * Fork - LGPL
55381  * <script type="text/javascript">
55382  */
55383
55384 // private - not really -- you end up using it !
55385 // This is a support class used internally by the Grid components
55386
55387 /**
55388  * @class Roo.grid.GridEditor
55389  * @extends Roo.Editor
55390  * Class for creating and editable grid elements.
55391  * @param {Object} config any settings (must include field)
55392  */
55393 Roo.grid.GridEditor = function(field, config){
55394     if (!config && field.field) {
55395         config = field;
55396         field = Roo.factory(config.field, Roo.form);
55397     }
55398     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55399     field.monitorTab = false;
55400 };
55401
55402 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55403     
55404     /**
55405      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55406      */
55407     
55408     alignment: "tl-tl",
55409     autoSize: "width",
55410     hideEl : false,
55411     cls: "x-small-editor x-grid-editor",
55412     shim:false,
55413     shadow:"frame"
55414 });/*
55415  * Based on:
55416  * Ext JS Library 1.1.1
55417  * Copyright(c) 2006-2007, Ext JS, LLC.
55418  *
55419  * Originally Released Under LGPL - original licence link has changed is not relivant.
55420  *
55421  * Fork - LGPL
55422  * <script type="text/javascript">
55423  */
55424   
55425
55426   
55427 Roo.grid.PropertyRecord = Roo.data.Record.create([
55428     {name:'name',type:'string'},  'value'
55429 ]);
55430
55431
55432 Roo.grid.PropertyStore = function(grid, source){
55433     this.grid = grid;
55434     this.store = new Roo.data.Store({
55435         recordType : Roo.grid.PropertyRecord
55436     });
55437     this.store.on('update', this.onUpdate,  this);
55438     if(source){
55439         this.setSource(source);
55440     }
55441     Roo.grid.PropertyStore.superclass.constructor.call(this);
55442 };
55443
55444
55445
55446 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55447     setSource : function(o){
55448         this.source = o;
55449         this.store.removeAll();
55450         var data = [];
55451         for(var k in o){
55452             if(this.isEditableValue(o[k])){
55453                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55454             }
55455         }
55456         this.store.loadRecords({records: data}, {}, true);
55457     },
55458
55459     onUpdate : function(ds, record, type){
55460         if(type == Roo.data.Record.EDIT){
55461             var v = record.data['value'];
55462             var oldValue = record.modified['value'];
55463             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55464                 this.source[record.id] = v;
55465                 record.commit();
55466                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55467             }else{
55468                 record.reject();
55469             }
55470         }
55471     },
55472
55473     getProperty : function(row){
55474        return this.store.getAt(row);
55475     },
55476
55477     isEditableValue: function(val){
55478         if(val && val instanceof Date){
55479             return true;
55480         }else if(typeof val == 'object' || typeof val == 'function'){
55481             return false;
55482         }
55483         return true;
55484     },
55485
55486     setValue : function(prop, value){
55487         this.source[prop] = value;
55488         this.store.getById(prop).set('value', value);
55489     },
55490
55491     getSource : function(){
55492         return this.source;
55493     }
55494 });
55495
55496 Roo.grid.PropertyColumnModel = function(grid, store){
55497     this.grid = grid;
55498     var g = Roo.grid;
55499     g.PropertyColumnModel.superclass.constructor.call(this, [
55500         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55501         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55502     ]);
55503     this.store = store;
55504     this.bselect = Roo.DomHelper.append(document.body, {
55505         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55506             {tag: 'option', value: 'true', html: 'true'},
55507             {tag: 'option', value: 'false', html: 'false'}
55508         ]
55509     });
55510     Roo.id(this.bselect);
55511     var f = Roo.form;
55512     this.editors = {
55513         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55514         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55515         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55516         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55517         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55518     };
55519     this.renderCellDelegate = this.renderCell.createDelegate(this);
55520     this.renderPropDelegate = this.renderProp.createDelegate(this);
55521 };
55522
55523 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55524     
55525     
55526     nameText : 'Name',
55527     valueText : 'Value',
55528     
55529     dateFormat : 'm/j/Y',
55530     
55531     
55532     renderDate : function(dateVal){
55533         return dateVal.dateFormat(this.dateFormat);
55534     },
55535
55536     renderBool : function(bVal){
55537         return bVal ? 'true' : 'false';
55538     },
55539
55540     isCellEditable : function(colIndex, rowIndex){
55541         return colIndex == 1;
55542     },
55543
55544     getRenderer : function(col){
55545         return col == 1 ?
55546             this.renderCellDelegate : this.renderPropDelegate;
55547     },
55548
55549     renderProp : function(v){
55550         return this.getPropertyName(v);
55551     },
55552
55553     renderCell : function(val){
55554         var rv = val;
55555         if(val instanceof Date){
55556             rv = this.renderDate(val);
55557         }else if(typeof val == 'boolean'){
55558             rv = this.renderBool(val);
55559         }
55560         return Roo.util.Format.htmlEncode(rv);
55561     },
55562
55563     getPropertyName : function(name){
55564         var pn = this.grid.propertyNames;
55565         return pn && pn[name] ? pn[name] : name;
55566     },
55567
55568     getCellEditor : function(colIndex, rowIndex){
55569         var p = this.store.getProperty(rowIndex);
55570         var n = p.data['name'], val = p.data['value'];
55571         
55572         if(typeof(this.grid.customEditors[n]) == 'string'){
55573             return this.editors[this.grid.customEditors[n]];
55574         }
55575         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55576             return this.grid.customEditors[n];
55577         }
55578         if(val instanceof Date){
55579             return this.editors['date'];
55580         }else if(typeof val == 'number'){
55581             return this.editors['number'];
55582         }else if(typeof val == 'boolean'){
55583             return this.editors['boolean'];
55584         }else{
55585             return this.editors['string'];
55586         }
55587     }
55588 });
55589
55590 /**
55591  * @class Roo.grid.PropertyGrid
55592  * @extends Roo.grid.EditorGrid
55593  * This class represents the  interface of a component based property grid control.
55594  * <br><br>Usage:<pre><code>
55595  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55596       
55597  });
55598  // set any options
55599  grid.render();
55600  * </code></pre>
55601   
55602  * @constructor
55603  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55604  * The container MUST have some type of size defined for the grid to fill. The container will be
55605  * automatically set to position relative if it isn't already.
55606  * @param {Object} config A config object that sets properties on this grid.
55607  */
55608 Roo.grid.PropertyGrid = function(container, config){
55609     config = config || {};
55610     var store = new Roo.grid.PropertyStore(this);
55611     this.store = store;
55612     var cm = new Roo.grid.PropertyColumnModel(this, store);
55613     store.store.sort('name', 'ASC');
55614     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55615         ds: store.store,
55616         cm: cm,
55617         enableColLock:false,
55618         enableColumnMove:false,
55619         stripeRows:false,
55620         trackMouseOver: false,
55621         clicksToEdit:1
55622     }, config));
55623     this.getGridEl().addClass('x-props-grid');
55624     this.lastEditRow = null;
55625     this.on('columnresize', this.onColumnResize, this);
55626     this.addEvents({
55627          /**
55628              * @event beforepropertychange
55629              * Fires before a property changes (return false to stop?)
55630              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55631              * @param {String} id Record Id
55632              * @param {String} newval New Value
55633          * @param {String} oldval Old Value
55634              */
55635         "beforepropertychange": true,
55636         /**
55637              * @event propertychange
55638              * Fires after a property changes
55639              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55640              * @param {String} id Record Id
55641              * @param {String} newval New Value
55642          * @param {String} oldval Old Value
55643              */
55644         "propertychange": true
55645     });
55646     this.customEditors = this.customEditors || {};
55647 };
55648 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55649     
55650      /**
55651      * @cfg {Object} customEditors map of colnames=> custom editors.
55652      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55653      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55654      * false disables editing of the field.
55655          */
55656     
55657       /**
55658      * @cfg {Object} propertyNames map of property Names to their displayed value
55659          */
55660     
55661     render : function(){
55662         Roo.grid.PropertyGrid.superclass.render.call(this);
55663         this.autoSize.defer(100, this);
55664     },
55665
55666     autoSize : function(){
55667         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55668         if(this.view){
55669             this.view.fitColumns();
55670         }
55671     },
55672
55673     onColumnResize : function(){
55674         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55675         this.autoSize();
55676     },
55677     /**
55678      * Sets the data for the Grid
55679      * accepts a Key => Value object of all the elements avaiable.
55680      * @param {Object} data  to appear in grid.
55681      */
55682     setSource : function(source){
55683         this.store.setSource(source);
55684         //this.autoSize();
55685     },
55686     /**
55687      * Gets all the data from the grid.
55688      * @return {Object} data  data stored in grid
55689      */
55690     getSource : function(){
55691         return this.store.getSource();
55692     }
55693 });/*
55694  * Based on:
55695  * Ext JS Library 1.1.1
55696  * Copyright(c) 2006-2007, Ext JS, LLC.
55697  *
55698  * Originally Released Under LGPL - original licence link has changed is not relivant.
55699  *
55700  * Fork - LGPL
55701  * <script type="text/javascript">
55702  */
55703  
55704 /**
55705  * @class Roo.LoadMask
55706  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55707  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55708  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55709  * element's UpdateManager load indicator and will be destroyed after the initial load.
55710  * @constructor
55711  * Create a new LoadMask
55712  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55713  * @param {Object} config The config object
55714  */
55715 Roo.LoadMask = function(el, config){
55716     this.el = Roo.get(el);
55717     Roo.apply(this, config);
55718     if(this.store){
55719         this.store.on('beforeload', this.onBeforeLoad, this);
55720         this.store.on('load', this.onLoad, this);
55721         this.store.on('loadexception', this.onLoadException, this);
55722         this.removeMask = false;
55723     }else{
55724         var um = this.el.getUpdateManager();
55725         um.showLoadIndicator = false; // disable the default indicator
55726         um.on('beforeupdate', this.onBeforeLoad, this);
55727         um.on('update', this.onLoad, this);
55728         um.on('failure', this.onLoad, this);
55729         this.removeMask = true;
55730     }
55731 };
55732
55733 Roo.LoadMask.prototype = {
55734     /**
55735      * @cfg {Boolean} removeMask
55736      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55737      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55738      */
55739     /**
55740      * @cfg {String} msg
55741      * The text to display in a centered loading message box (defaults to 'Loading...')
55742      */
55743     msg : 'Loading...',
55744     /**
55745      * @cfg {String} msgCls
55746      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55747      */
55748     msgCls : 'x-mask-loading',
55749
55750     /**
55751      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55752      * @type Boolean
55753      */
55754     disabled: false,
55755
55756     /**
55757      * Disables the mask to prevent it from being displayed
55758      */
55759     disable : function(){
55760        this.disabled = true;
55761     },
55762
55763     /**
55764      * Enables the mask so that it can be displayed
55765      */
55766     enable : function(){
55767         this.disabled = false;
55768     },
55769     
55770     onLoadException : function()
55771     {
55772         Roo.log(arguments);
55773         
55774         if (typeof(arguments[3]) != 'undefined') {
55775             Roo.MessageBox.alert("Error loading",arguments[3]);
55776         } 
55777         /*
55778         try {
55779             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55780                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55781             }   
55782         } catch(e) {
55783             
55784         }
55785         */
55786     
55787         
55788         
55789         this.el.unmask(this.removeMask);
55790     },
55791     // private
55792     onLoad : function()
55793     {
55794         this.el.unmask(this.removeMask);
55795     },
55796
55797     // private
55798     onBeforeLoad : function(){
55799         if(!this.disabled){
55800             this.el.mask(this.msg, this.msgCls);
55801         }
55802     },
55803
55804     // private
55805     destroy : function(){
55806         if(this.store){
55807             this.store.un('beforeload', this.onBeforeLoad, this);
55808             this.store.un('load', this.onLoad, this);
55809             this.store.un('loadexception', this.onLoadException, this);
55810         }else{
55811             var um = this.el.getUpdateManager();
55812             um.un('beforeupdate', this.onBeforeLoad, this);
55813             um.un('update', this.onLoad, this);
55814             um.un('failure', this.onLoad, this);
55815         }
55816     }
55817 };/*
55818  * Based on:
55819  * Ext JS Library 1.1.1
55820  * Copyright(c) 2006-2007, Ext JS, LLC.
55821  *
55822  * Originally Released Under LGPL - original licence link has changed is not relivant.
55823  *
55824  * Fork - LGPL
55825  * <script type="text/javascript">
55826  */
55827
55828
55829 /**
55830  * @class Roo.XTemplate
55831  * @extends Roo.Template
55832  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55833 <pre><code>
55834 var t = new Roo.XTemplate(
55835         '&lt;select name="{name}"&gt;',
55836                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55837         '&lt;/select&gt;'
55838 );
55839  
55840 // then append, applying the master template values
55841  </code></pre>
55842  *
55843  * Supported features:
55844  *
55845  *  Tags:
55846
55847 <pre><code>
55848       {a_variable} - output encoded.
55849       {a_variable.format:("Y-m-d")} - call a method on the variable
55850       {a_variable:raw} - unencoded output
55851       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55852       {a_variable:this.method_on_template(...)} - call a method on the template object.
55853  
55854 </code></pre>
55855  *  The tpl tag:
55856 <pre><code>
55857         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55858         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55859         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55860         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55861   
55862         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55863         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55864 </code></pre>
55865  *      
55866  */
55867 Roo.XTemplate = function()
55868 {
55869     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55870     if (this.html) {
55871         this.compile();
55872     }
55873 };
55874
55875
55876 Roo.extend(Roo.XTemplate, Roo.Template, {
55877
55878     /**
55879      * The various sub templates
55880      */
55881     tpls : false,
55882     /**
55883      *
55884      * basic tag replacing syntax
55885      * WORD:WORD()
55886      *
55887      * // you can fake an object call by doing this
55888      *  x.t:(test,tesT) 
55889      * 
55890      */
55891     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55892
55893     /**
55894      * compile the template
55895      *
55896      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55897      *
55898      */
55899     compile: function()
55900     {
55901         var s = this.html;
55902      
55903         s = ['<tpl>', s, '</tpl>'].join('');
55904     
55905         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55906             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55907             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55908             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55909             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55910             m,
55911             id     = 0,
55912             tpls   = [];
55913     
55914         while(true == !!(m = s.match(re))){
55915             var forMatch   = m[0].match(nameRe),
55916                 ifMatch   = m[0].match(ifRe),
55917                 execMatch   = m[0].match(execRe),
55918                 namedMatch   = m[0].match(namedRe),
55919                 
55920                 exp  = null, 
55921                 fn   = null,
55922                 exec = null,
55923                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55924                 
55925             if (ifMatch) {
55926                 // if - puts fn into test..
55927                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55928                 if(exp){
55929                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55930                 }
55931             }
55932             
55933             if (execMatch) {
55934                 // exec - calls a function... returns empty if true is  returned.
55935                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55936                 if(exp){
55937                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55938                 }
55939             }
55940             
55941             
55942             if (name) {
55943                 // for = 
55944                 switch(name){
55945                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
55946                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
55947                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
55948                 }
55949             }
55950             var uid = namedMatch ? namedMatch[1] : id;
55951             
55952             
55953             tpls.push({
55954                 id:     namedMatch ? namedMatch[1] : id,
55955                 target: name,
55956                 exec:   exec,
55957                 test:   fn,
55958                 body:   m[1] || ''
55959             });
55960             if (namedMatch) {
55961                 s = s.replace(m[0], '');
55962             } else { 
55963                 s = s.replace(m[0], '{xtpl'+ id + '}');
55964             }
55965             ++id;
55966         }
55967         this.tpls = [];
55968         for(var i = tpls.length-1; i >= 0; --i){
55969             this.compileTpl(tpls[i]);
55970             this.tpls[tpls[i].id] = tpls[i];
55971         }
55972         this.master = tpls[tpls.length-1];
55973         return this;
55974     },
55975     /**
55976      * same as applyTemplate, except it's done to one of the subTemplates
55977      * when using named templates, you can do:
55978      *
55979      * var str = pl.applySubTemplate('your-name', values);
55980      *
55981      * 
55982      * @param {Number} id of the template
55983      * @param {Object} values to apply to template
55984      * @param {Object} parent (normaly the instance of this object)
55985      */
55986     applySubTemplate : function(id, values, parent)
55987     {
55988         
55989         
55990         var t = this.tpls[id];
55991         
55992         
55993         try { 
55994             if(t.test && !t.test.call(this, values, parent)){
55995                 return '';
55996             }
55997         } catch(e) {
55998             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
55999             Roo.log(e.toString());
56000             Roo.log(t.test);
56001             return ''
56002         }
56003         try { 
56004             
56005             if(t.exec && t.exec.call(this, values, parent)){
56006                 return '';
56007             }
56008         } catch(e) {
56009             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
56010             Roo.log(e.toString());
56011             Roo.log(t.exec);
56012             return ''
56013         }
56014         try {
56015             var vs = t.target ? t.target.call(this, values, parent) : values;
56016             parent = t.target ? values : parent;
56017             if(t.target && vs instanceof Array){
56018                 var buf = [];
56019                 for(var i = 0, len = vs.length; i < len; i++){
56020                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
56021                 }
56022                 return buf.join('');
56023             }
56024             return t.compiled.call(this, vs, parent);
56025         } catch (e) {
56026             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
56027             Roo.log(e.toString());
56028             Roo.log(t.compiled);
56029             return '';
56030         }
56031     },
56032
56033     compileTpl : function(tpl)
56034     {
56035         var fm = Roo.util.Format;
56036         var useF = this.disableFormats !== true;
56037         var sep = Roo.isGecko ? "+" : ",";
56038         var undef = function(str) {
56039             Roo.log("Property not found :"  + str);
56040             return '';
56041         };
56042         
56043         var fn = function(m, name, format, args)
56044         {
56045             //Roo.log(arguments);
56046             args = args ? args.replace(/\\'/g,"'") : args;
56047             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
56048             if (typeof(format) == 'undefined') {
56049                 format= 'htmlEncode';
56050             }
56051             if (format == 'raw' ) {
56052                 format = false;
56053             }
56054             
56055             if(name.substr(0, 4) == 'xtpl'){
56056                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
56057             }
56058             
56059             // build an array of options to determine if value is undefined..
56060             
56061             // basically get 'xxxx.yyyy' then do
56062             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
56063             //    (function () { Roo.log("Property not found"); return ''; })() :
56064             //    ......
56065             
56066             var udef_ar = [];
56067             var lookfor = '';
56068             Roo.each(name.split('.'), function(st) {
56069                 lookfor += (lookfor.length ? '.': '') + st;
56070                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
56071             });
56072             
56073             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
56074             
56075             
56076             if(format && useF){
56077                 
56078                 args = args ? ',' + args : "";
56079                  
56080                 if(format.substr(0, 5) != "this."){
56081                     format = "fm." + format + '(';
56082                 }else{
56083                     format = 'this.call("'+ format.substr(5) + '", ';
56084                     args = ", values";
56085                 }
56086                 
56087                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
56088             }
56089              
56090             if (args.length) {
56091                 // called with xxyx.yuu:(test,test)
56092                 // change to ()
56093                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
56094             }
56095             // raw.. - :raw modifier..
56096             return "'"+ sep + udef_st  + name + ")"+sep+"'";
56097             
56098         };
56099         var body;
56100         // branched to use + in gecko and [].join() in others
56101         if(Roo.isGecko){
56102             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
56103                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
56104                     "';};};";
56105         }else{
56106             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
56107             body.push(tpl.body.replace(/(\r\n|\n)/g,
56108                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
56109             body.push("'].join('');};};");
56110             body = body.join('');
56111         }
56112         
56113         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
56114        
56115         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
56116         eval(body);
56117         
56118         return this;
56119     },
56120
56121     applyTemplate : function(values){
56122         return this.master.compiled.call(this, values, {});
56123         //var s = this.subs;
56124     },
56125
56126     apply : function(){
56127         return this.applyTemplate.apply(this, arguments);
56128     }
56129
56130  });
56131
56132 Roo.XTemplate.from = function(el){
56133     el = Roo.getDom(el);
56134     return new Roo.XTemplate(el.value || el.innerHTML);
56135 };