Roo/dd/DDProxy.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.resetValue);
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         this.resetValue =  this.getValue();
37046     },
37047
37048     // private
37049     onFocus : function(){
37050         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37051             this.el.addClass(this.focusClass);
37052         }
37053         if(!this.hasFocus){
37054             this.hasFocus = true;
37055             this.startValue = this.getValue();
37056             this.fireEvent("focus", this);
37057         }
37058     },
37059
37060     beforeBlur : Roo.emptyFn,
37061
37062     // private
37063     onBlur : function(){
37064         this.beforeBlur();
37065         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37066             this.el.removeClass(this.focusClass);
37067         }
37068         this.hasFocus = false;
37069         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37070             this.validate();
37071         }
37072         var v = this.getValue();
37073         if(String(v) !== String(this.startValue)){
37074             this.fireEvent('change', this, v, this.startValue);
37075         }
37076         this.fireEvent("blur", this);
37077     },
37078
37079     /**
37080      * Returns whether or not the field value is currently valid
37081      * @param {Boolean} preventMark True to disable marking the field invalid
37082      * @return {Boolean} True if the value is valid, else false
37083      */
37084     isValid : function(preventMark){
37085         if(this.disabled){
37086             return true;
37087         }
37088         var restore = this.preventMark;
37089         this.preventMark = preventMark === true;
37090         var v = this.validateValue(this.processValue(this.getRawValue()));
37091         this.preventMark = restore;
37092         return v;
37093     },
37094
37095     /**
37096      * Validates the field value
37097      * @return {Boolean} True if the value is valid, else false
37098      */
37099     validate : function(){
37100         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37101             this.clearInvalid();
37102             return true;
37103         }
37104         return false;
37105     },
37106
37107     processValue : function(value){
37108         return value;
37109     },
37110
37111     // private
37112     // Subclasses should provide the validation implementation by overriding this
37113     validateValue : function(value){
37114         return true;
37115     },
37116
37117     /**
37118      * Mark this field as invalid
37119      * @param {String} msg The validation message
37120      */
37121     markInvalid : function(msg){
37122         if(!this.rendered || this.preventMark){ // not rendered
37123             return;
37124         }
37125         
37126         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37127         
37128         obj.el.addClass(this.invalidClass);
37129         msg = msg || this.invalidText;
37130         switch(this.msgTarget){
37131             case 'qtip':
37132                 obj.el.dom.qtip = msg;
37133                 obj.el.dom.qclass = 'x-form-invalid-tip';
37134                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37135                     Roo.QuickTips.enable();
37136                 }
37137                 break;
37138             case 'title':
37139                 this.el.dom.title = msg;
37140                 break;
37141             case 'under':
37142                 if(!this.errorEl){
37143                     var elp = this.el.findParent('.x-form-element', 5, true);
37144                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37145                     this.errorEl.setWidth(elp.getWidth(true)-20);
37146                 }
37147                 this.errorEl.update(msg);
37148                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37149                 break;
37150             case 'side':
37151                 if(!this.errorIcon){
37152                     var elp = this.el.findParent('.x-form-element', 5, true);
37153                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37154                 }
37155                 this.alignErrorIcon();
37156                 this.errorIcon.dom.qtip = msg;
37157                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37158                 this.errorIcon.show();
37159                 this.on('resize', this.alignErrorIcon, this);
37160                 break;
37161             default:
37162                 var t = Roo.getDom(this.msgTarget);
37163                 t.innerHTML = msg;
37164                 t.style.display = this.msgDisplay;
37165                 break;
37166         }
37167         this.fireEvent('invalid', this, msg);
37168     },
37169
37170     // private
37171     alignErrorIcon : function(){
37172         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37173     },
37174
37175     /**
37176      * Clear any invalid styles/messages for this field
37177      */
37178     clearInvalid : function(){
37179         if(!this.rendered || this.preventMark){ // not rendered
37180             return;
37181         }
37182         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37183         
37184         obj.el.removeClass(this.invalidClass);
37185         switch(this.msgTarget){
37186             case 'qtip':
37187                 obj.el.dom.qtip = '';
37188                 break;
37189             case 'title':
37190                 this.el.dom.title = '';
37191                 break;
37192             case 'under':
37193                 if(this.errorEl){
37194                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37195                 }
37196                 break;
37197             case 'side':
37198                 if(this.errorIcon){
37199                     this.errorIcon.dom.qtip = '';
37200                     this.errorIcon.hide();
37201                     this.un('resize', this.alignErrorIcon, this);
37202                 }
37203                 break;
37204             default:
37205                 var t = Roo.getDom(this.msgTarget);
37206                 t.innerHTML = '';
37207                 t.style.display = 'none';
37208                 break;
37209         }
37210         this.fireEvent('valid', this);
37211     },
37212
37213     /**
37214      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37215      * @return {Mixed} value The field value
37216      */
37217     getRawValue : function(){
37218         var v = this.el.getValue();
37219         
37220         return v;
37221     },
37222
37223     /**
37224      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37225      * @return {Mixed} value The field value
37226      */
37227     getValue : function(){
37228         var v = this.el.getValue();
37229          
37230         return v;
37231     },
37232
37233     /**
37234      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37235      * @param {Mixed} value The value to set
37236      */
37237     setRawValue : function(v){
37238         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37239     },
37240
37241     /**
37242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37243      * @param {Mixed} value The value to set
37244      */
37245     setValue : function(v){
37246         this.value = v;
37247         if(this.rendered){
37248             this.el.dom.value = (v === null || v === undefined ? '' : v);
37249              this.validate();
37250         }
37251     },
37252
37253     adjustSize : function(w, h){
37254         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37255         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37256         return s;
37257     },
37258
37259     adjustWidth : function(tag, w){
37260         tag = tag.toLowerCase();
37261         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37262             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37263                 if(tag == 'input'){
37264                     return w + 2;
37265                 }
37266                 if(tag == 'textarea'){
37267                     return w-2;
37268                 }
37269             }else if(Roo.isOpera){
37270                 if(tag == 'input'){
37271                     return w + 2;
37272                 }
37273                 if(tag == 'textarea'){
37274                     return w-2;
37275                 }
37276             }
37277         }
37278         return w;
37279     }
37280 });
37281
37282
37283 // anything other than normal should be considered experimental
37284 Roo.form.Field.msgFx = {
37285     normal : {
37286         show: function(msgEl, f){
37287             msgEl.setDisplayed('block');
37288         },
37289
37290         hide : function(msgEl, f){
37291             msgEl.setDisplayed(false).update('');
37292         }
37293     },
37294
37295     slide : {
37296         show: function(msgEl, f){
37297             msgEl.slideIn('t', {stopFx:true});
37298         },
37299
37300         hide : function(msgEl, f){
37301             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37302         }
37303     },
37304
37305     slideRight : {
37306         show: function(msgEl, f){
37307             msgEl.fixDisplay();
37308             msgEl.alignTo(f.el, 'tl-tr');
37309             msgEl.slideIn('l', {stopFx:true});
37310         },
37311
37312         hide : function(msgEl, f){
37313             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37314         }
37315     }
37316 };/*
37317  * Based on:
37318  * Ext JS Library 1.1.1
37319  * Copyright(c) 2006-2007, Ext JS, LLC.
37320  *
37321  * Originally Released Under LGPL - original licence link has changed is not relivant.
37322  *
37323  * Fork - LGPL
37324  * <script type="text/javascript">
37325  */
37326  
37327
37328 /**
37329  * @class Roo.form.TextField
37330  * @extends Roo.form.Field
37331  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37332  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37333  * @constructor
37334  * Creates a new TextField
37335  * @param {Object} config Configuration options
37336  */
37337 Roo.form.TextField = function(config){
37338     Roo.form.TextField.superclass.constructor.call(this, config);
37339     this.addEvents({
37340         /**
37341          * @event autosize
37342          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37343          * according to the default logic, but this event provides a hook for the developer to apply additional
37344          * logic at runtime to resize the field if needed.
37345              * @param {Roo.form.Field} this This text field
37346              * @param {Number} width The new field width
37347              */
37348         autosize : true
37349     });
37350 };
37351
37352 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37353     /**
37354      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37355      */
37356     grow : false,
37357     /**
37358      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37359      */
37360     growMin : 30,
37361     /**
37362      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37363      */
37364     growMax : 800,
37365     /**
37366      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37367      */
37368     vtype : null,
37369     /**
37370      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37371      */
37372     maskRe : null,
37373     /**
37374      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37375      */
37376     disableKeyFilter : false,
37377     /**
37378      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37379      */
37380     allowBlank : true,
37381     /**
37382      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37383      */
37384     minLength : 0,
37385     /**
37386      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37387      */
37388     maxLength : Number.MAX_VALUE,
37389     /**
37390      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37391      */
37392     minLengthText : "The minimum length for this field is {0}",
37393     /**
37394      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37395      */
37396     maxLengthText : "The maximum length for this field is {0}",
37397     /**
37398      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37399      */
37400     selectOnFocus : false,
37401     /**
37402      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37403      */
37404     blankText : "This field is required",
37405     /**
37406      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37407      * If available, this function will be called only after the basic validators all return true, and will be passed the
37408      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37409      */
37410     validator : null,
37411     /**
37412      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37413      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37414      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37415      */
37416     regex : null,
37417     /**
37418      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37419      */
37420     regexText : "",
37421     /**
37422      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37423      */
37424     emptyText : null,
37425    
37426
37427     // private
37428     initEvents : function()
37429     {
37430         if (this.emptyText) {
37431             this.el.attr('placeholder', this.emptyText);
37432         }
37433         
37434         Roo.form.TextField.superclass.initEvents.call(this);
37435         if(this.validationEvent == 'keyup'){
37436             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37437             this.el.on('keyup', this.filterValidation, this);
37438         }
37439         else if(this.validationEvent !== false){
37440             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37441         }
37442         
37443         if(this.selectOnFocus){
37444             this.on("focus", this.preFocus, this);
37445             
37446         }
37447         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37448             this.el.on("keypress", this.filterKeys, this);
37449         }
37450         if(this.grow){
37451             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37452             this.el.on("click", this.autoSize,  this);
37453         }
37454         if(this.el.is('input[type=password]') && Roo.isSafari){
37455             this.el.on('keydown', this.SafariOnKeyDown, this);
37456         }
37457     },
37458
37459     processValue : function(value){
37460         if(this.stripCharsRe){
37461             var newValue = value.replace(this.stripCharsRe, '');
37462             if(newValue !== value){
37463                 this.setRawValue(newValue);
37464                 return newValue;
37465             }
37466         }
37467         return value;
37468     },
37469
37470     filterValidation : function(e){
37471         if(!e.isNavKeyPress()){
37472             this.validationTask.delay(this.validationDelay);
37473         }
37474     },
37475
37476     // private
37477     onKeyUp : function(e){
37478         if(!e.isNavKeyPress()){
37479             this.autoSize();
37480         }
37481     },
37482
37483     /**
37484      * Resets the current field value to the originally-loaded value and clears any validation messages.
37485      *  
37486      */
37487     reset : function(){
37488         Roo.form.TextField.superclass.reset.call(this);
37489        
37490     },
37491
37492     
37493     // private
37494     preFocus : function(){
37495         
37496         if(this.selectOnFocus){
37497             this.el.dom.select();
37498         }
37499     },
37500
37501     
37502     // private
37503     filterKeys : function(e){
37504         var k = e.getKey();
37505         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37506             return;
37507         }
37508         var c = e.getCharCode(), cc = String.fromCharCode(c);
37509         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37510             return;
37511         }
37512         if(!this.maskRe.test(cc)){
37513             e.stopEvent();
37514         }
37515     },
37516
37517     setValue : function(v){
37518         
37519         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37520         
37521         this.autoSize();
37522     },
37523
37524     /**
37525      * Validates a value according to the field's validation rules and marks the field as invalid
37526      * if the validation fails
37527      * @param {Mixed} value The value to validate
37528      * @return {Boolean} True if the value is valid, else false
37529      */
37530     validateValue : function(value){
37531         if(value.length < 1)  { // if it's blank
37532              if(this.allowBlank){
37533                 this.clearInvalid();
37534                 return true;
37535              }else{
37536                 this.markInvalid(this.blankText);
37537                 return false;
37538              }
37539         }
37540         if(value.length < this.minLength){
37541             this.markInvalid(String.format(this.minLengthText, this.minLength));
37542             return false;
37543         }
37544         if(value.length > this.maxLength){
37545             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37546             return false;
37547         }
37548         if(this.vtype){
37549             var vt = Roo.form.VTypes;
37550             if(!vt[this.vtype](value, this)){
37551                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37552                 return false;
37553             }
37554         }
37555         if(typeof this.validator == "function"){
37556             var msg = this.validator(value);
37557             if(msg !== true){
37558                 this.markInvalid(msg);
37559                 return false;
37560             }
37561         }
37562         if(this.regex && !this.regex.test(value)){
37563             this.markInvalid(this.regexText);
37564             return false;
37565         }
37566         return true;
37567     },
37568
37569     /**
37570      * Selects text in this field
37571      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37572      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37573      */
37574     selectText : function(start, end){
37575         var v = this.getRawValue();
37576         if(v.length > 0){
37577             start = start === undefined ? 0 : start;
37578             end = end === undefined ? v.length : end;
37579             var d = this.el.dom;
37580             if(d.setSelectionRange){
37581                 d.setSelectionRange(start, end);
37582             }else if(d.createTextRange){
37583                 var range = d.createTextRange();
37584                 range.moveStart("character", start);
37585                 range.moveEnd("character", v.length-end);
37586                 range.select();
37587             }
37588         }
37589     },
37590
37591     /**
37592      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37593      * This only takes effect if grow = true, and fires the autosize event.
37594      */
37595     autoSize : function(){
37596         if(!this.grow || !this.rendered){
37597             return;
37598         }
37599         if(!this.metrics){
37600             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37601         }
37602         var el = this.el;
37603         var v = el.dom.value;
37604         var d = document.createElement('div');
37605         d.appendChild(document.createTextNode(v));
37606         v = d.innerHTML;
37607         d = null;
37608         v += "&#160;";
37609         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37610         this.el.setWidth(w);
37611         this.fireEvent("autosize", this, w);
37612     },
37613     
37614     // private
37615     SafariOnKeyDown : function(event)
37616     {
37617         // this is a workaround for a password hang bug on chrome/ webkit.
37618         
37619         var isSelectAll = false;
37620         
37621         if(this.el.dom.selectionEnd > 0){
37622             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37623         }
37624         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37625             event.preventDefault();
37626             this.setValue('');
37627             return;
37628         }
37629         
37630         if(isSelectAll){ // backspace and delete key
37631             
37632             event.preventDefault();
37633             // this is very hacky as keydown always get's upper case.
37634             //
37635             var cc = String.fromCharCode(event.getCharCode());
37636             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37637             
37638         }
37639         
37640         
37641     }
37642 });/*
37643  * Based on:
37644  * Ext JS Library 1.1.1
37645  * Copyright(c) 2006-2007, Ext JS, LLC.
37646  *
37647  * Originally Released Under LGPL - original licence link has changed is not relivant.
37648  *
37649  * Fork - LGPL
37650  * <script type="text/javascript">
37651  */
37652  
37653 /**
37654  * @class Roo.form.Hidden
37655  * @extends Roo.form.TextField
37656  * Simple Hidden element used on forms 
37657  * 
37658  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37659  * 
37660  * @constructor
37661  * Creates a new Hidden form element.
37662  * @param {Object} config Configuration options
37663  */
37664
37665
37666
37667 // easy hidden field...
37668 Roo.form.Hidden = function(config){
37669     Roo.form.Hidden.superclass.constructor.call(this, config);
37670 };
37671   
37672 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37673     fieldLabel:      '',
37674     inputType:      'hidden',
37675     width:          50,
37676     allowBlank:     true,
37677     labelSeparator: '',
37678     hidden:         true,
37679     itemCls :       'x-form-item-display-none'
37680
37681
37682 });
37683
37684
37685 /*
37686  * Based on:
37687  * Ext JS Library 1.1.1
37688  * Copyright(c) 2006-2007, Ext JS, LLC.
37689  *
37690  * Originally Released Under LGPL - original licence link has changed is not relivant.
37691  *
37692  * Fork - LGPL
37693  * <script type="text/javascript">
37694  */
37695  
37696 /**
37697  * @class Roo.form.TriggerField
37698  * @extends Roo.form.TextField
37699  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37700  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37701  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37702  * for which you can provide a custom implementation.  For example:
37703  * <pre><code>
37704 var trigger = new Roo.form.TriggerField();
37705 trigger.onTriggerClick = myTriggerFn;
37706 trigger.applyTo('my-field');
37707 </code></pre>
37708  *
37709  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37710  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37711  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37712  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37713  * @constructor
37714  * Create a new TriggerField.
37715  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37716  * to the base TextField)
37717  */
37718 Roo.form.TriggerField = function(config){
37719     this.mimicing = false;
37720     Roo.form.TriggerField.superclass.constructor.call(this, config);
37721 };
37722
37723 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37724     /**
37725      * @cfg {String} triggerClass A CSS class to apply to the trigger
37726      */
37727     /**
37728      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37729      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37730      */
37731     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37732     /**
37733      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37734      */
37735     hideTrigger:false,
37736
37737     /** @cfg {Boolean} grow @hide */
37738     /** @cfg {Number} growMin @hide */
37739     /** @cfg {Number} growMax @hide */
37740
37741     /**
37742      * @hide 
37743      * @method
37744      */
37745     autoSize: Roo.emptyFn,
37746     // private
37747     monitorTab : true,
37748     // private
37749     deferHeight : true,
37750
37751     
37752     actionMode : 'wrap',
37753     // private
37754     onResize : function(w, h){
37755         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37756         if(typeof w == 'number'){
37757             var x = w - this.trigger.getWidth();
37758             this.el.setWidth(this.adjustWidth('input', x));
37759             this.trigger.setStyle('left', x+'px');
37760         }
37761     },
37762
37763     // private
37764     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37765
37766     // private
37767     getResizeEl : function(){
37768         return this.wrap;
37769     },
37770
37771     // private
37772     getPositionEl : function(){
37773         return this.wrap;
37774     },
37775
37776     // private
37777     alignErrorIcon : function(){
37778         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37779     },
37780
37781     // private
37782     onRender : function(ct, position){
37783         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37784         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37785         this.trigger = this.wrap.createChild(this.triggerConfig ||
37786                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37787         if(this.hideTrigger){
37788             this.trigger.setDisplayed(false);
37789         }
37790         this.initTrigger();
37791         if(!this.width){
37792             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37793         }
37794     },
37795
37796     // private
37797     initTrigger : function(){
37798         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37799         this.trigger.addClassOnOver('x-form-trigger-over');
37800         this.trigger.addClassOnClick('x-form-trigger-click');
37801     },
37802
37803     // private
37804     onDestroy : function(){
37805         if(this.trigger){
37806             this.trigger.removeAllListeners();
37807             this.trigger.remove();
37808         }
37809         if(this.wrap){
37810             this.wrap.remove();
37811         }
37812         Roo.form.TriggerField.superclass.onDestroy.call(this);
37813     },
37814
37815     // private
37816     onFocus : function(){
37817         Roo.form.TriggerField.superclass.onFocus.call(this);
37818         if(!this.mimicing){
37819             this.wrap.addClass('x-trigger-wrap-focus');
37820             this.mimicing = true;
37821             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37822             if(this.monitorTab){
37823                 this.el.on("keydown", this.checkTab, this);
37824             }
37825         }
37826     },
37827
37828     // private
37829     checkTab : function(e){
37830         if(e.getKey() == e.TAB){
37831             this.triggerBlur();
37832         }
37833     },
37834
37835     // private
37836     onBlur : function(){
37837         // do nothing
37838     },
37839
37840     // private
37841     mimicBlur : function(e, t){
37842         if(!this.wrap.contains(t) && this.validateBlur()){
37843             this.triggerBlur();
37844         }
37845     },
37846
37847     // private
37848     triggerBlur : function(){
37849         this.mimicing = false;
37850         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37851         if(this.monitorTab){
37852             this.el.un("keydown", this.checkTab, this);
37853         }
37854         this.wrap.removeClass('x-trigger-wrap-focus');
37855         Roo.form.TriggerField.superclass.onBlur.call(this);
37856     },
37857
37858     // private
37859     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37860     validateBlur : function(e, t){
37861         return true;
37862     },
37863
37864     // private
37865     onDisable : function(){
37866         Roo.form.TriggerField.superclass.onDisable.call(this);
37867         if(this.wrap){
37868             this.wrap.addClass('x-item-disabled');
37869         }
37870     },
37871
37872     // private
37873     onEnable : function(){
37874         Roo.form.TriggerField.superclass.onEnable.call(this);
37875         if(this.wrap){
37876             this.wrap.removeClass('x-item-disabled');
37877         }
37878     },
37879
37880     // private
37881     onShow : function(){
37882         var ae = this.getActionEl();
37883         
37884         if(ae){
37885             ae.dom.style.display = '';
37886             ae.dom.style.visibility = 'visible';
37887         }
37888     },
37889
37890     // private
37891     
37892     onHide : function(){
37893         var ae = this.getActionEl();
37894         ae.dom.style.display = 'none';
37895     },
37896
37897     /**
37898      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37899      * by an implementing function.
37900      * @method
37901      * @param {EventObject} e
37902      */
37903     onTriggerClick : Roo.emptyFn
37904 });
37905
37906 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37907 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37908 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37909 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37910     initComponent : function(){
37911         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37912
37913         this.triggerConfig = {
37914             tag:'span', cls:'x-form-twin-triggers', cn:[
37915             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37916             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37917         ]};
37918     },
37919
37920     getTrigger : function(index){
37921         return this.triggers[index];
37922     },
37923
37924     initTrigger : function(){
37925         var ts = this.trigger.select('.x-form-trigger', true);
37926         this.wrap.setStyle('overflow', 'hidden');
37927         var triggerField = this;
37928         ts.each(function(t, all, index){
37929             t.hide = function(){
37930                 var w = triggerField.wrap.getWidth();
37931                 this.dom.style.display = 'none';
37932                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37933             };
37934             t.show = function(){
37935                 var w = triggerField.wrap.getWidth();
37936                 this.dom.style.display = '';
37937                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37938             };
37939             var triggerIndex = 'Trigger'+(index+1);
37940
37941             if(this['hide'+triggerIndex]){
37942                 t.dom.style.display = 'none';
37943             }
37944             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37945             t.addClassOnOver('x-form-trigger-over');
37946             t.addClassOnClick('x-form-trigger-click');
37947         }, this);
37948         this.triggers = ts.elements;
37949     },
37950
37951     onTrigger1Click : Roo.emptyFn,
37952     onTrigger2Click : Roo.emptyFn
37953 });/*
37954  * Based on:
37955  * Ext JS Library 1.1.1
37956  * Copyright(c) 2006-2007, Ext JS, LLC.
37957  *
37958  * Originally Released Under LGPL - original licence link has changed is not relivant.
37959  *
37960  * Fork - LGPL
37961  * <script type="text/javascript">
37962  */
37963  
37964 /**
37965  * @class Roo.form.TextArea
37966  * @extends Roo.form.TextField
37967  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37968  * support for auto-sizing.
37969  * @constructor
37970  * Creates a new TextArea
37971  * @param {Object} config Configuration options
37972  */
37973 Roo.form.TextArea = function(config){
37974     Roo.form.TextArea.superclass.constructor.call(this, config);
37975     // these are provided exchanges for backwards compat
37976     // minHeight/maxHeight were replaced by growMin/growMax to be
37977     // compatible with TextField growing config values
37978     if(this.minHeight !== undefined){
37979         this.growMin = this.minHeight;
37980     }
37981     if(this.maxHeight !== undefined){
37982         this.growMax = this.maxHeight;
37983     }
37984 };
37985
37986 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37987     /**
37988      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37989      */
37990     growMin : 60,
37991     /**
37992      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37993      */
37994     growMax: 1000,
37995     /**
37996      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37997      * in the field (equivalent to setting overflow: hidden, defaults to false)
37998      */
37999     preventScrollbars: false,
38000     /**
38001      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38002      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38003      */
38004
38005     // private
38006     onRender : function(ct, position){
38007         if(!this.el){
38008             this.defaultAutoCreate = {
38009                 tag: "textarea",
38010                 style:"width:300px;height:60px;",
38011                 autocomplete: "off"
38012             };
38013         }
38014         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38015         if(this.grow){
38016             this.textSizeEl = Roo.DomHelper.append(document.body, {
38017                 tag: "pre", cls: "x-form-grow-sizer"
38018             });
38019             if(this.preventScrollbars){
38020                 this.el.setStyle("overflow", "hidden");
38021             }
38022             this.el.setHeight(this.growMin);
38023         }
38024     },
38025
38026     onDestroy : function(){
38027         if(this.textSizeEl){
38028             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38029         }
38030         Roo.form.TextArea.superclass.onDestroy.call(this);
38031     },
38032
38033     // private
38034     onKeyUp : function(e){
38035         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38036             this.autoSize();
38037         }
38038     },
38039
38040     /**
38041      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38042      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38043      */
38044     autoSize : function(){
38045         if(!this.grow || !this.textSizeEl){
38046             return;
38047         }
38048         var el = this.el;
38049         var v = el.dom.value;
38050         var ts = this.textSizeEl;
38051
38052         ts.innerHTML = '';
38053         ts.appendChild(document.createTextNode(v));
38054         v = ts.innerHTML;
38055
38056         Roo.fly(ts).setWidth(this.el.getWidth());
38057         if(v.length < 1){
38058             v = "&#160;&#160;";
38059         }else{
38060             if(Roo.isIE){
38061                 v = v.replace(/\n/g, '<p>&#160;</p>');
38062             }
38063             v += "&#160;\n&#160;";
38064         }
38065         ts.innerHTML = v;
38066         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38067         if(h != this.lastHeight){
38068             this.lastHeight = h;
38069             this.el.setHeight(h);
38070             this.fireEvent("autosize", this, h);
38071         }
38072     }
38073 });/*
38074  * Based on:
38075  * Ext JS Library 1.1.1
38076  * Copyright(c) 2006-2007, Ext JS, LLC.
38077  *
38078  * Originally Released Under LGPL - original licence link has changed is not relivant.
38079  *
38080  * Fork - LGPL
38081  * <script type="text/javascript">
38082  */
38083  
38084
38085 /**
38086  * @class Roo.form.NumberField
38087  * @extends Roo.form.TextField
38088  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38089  * @constructor
38090  * Creates a new NumberField
38091  * @param {Object} config Configuration options
38092  */
38093 Roo.form.NumberField = function(config){
38094     Roo.form.NumberField.superclass.constructor.call(this, config);
38095 };
38096
38097 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38098     /**
38099      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38100      */
38101     fieldClass: "x-form-field x-form-num-field",
38102     /**
38103      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38104      */
38105     allowDecimals : true,
38106     /**
38107      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38108      */
38109     decimalSeparator : ".",
38110     /**
38111      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38112      */
38113     decimalPrecision : 2,
38114     /**
38115      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38116      */
38117     allowNegative : true,
38118     /**
38119      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38120      */
38121     minValue : Number.NEGATIVE_INFINITY,
38122     /**
38123      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38124      */
38125     maxValue : Number.MAX_VALUE,
38126     /**
38127      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38128      */
38129     minText : "The minimum value for this field is {0}",
38130     /**
38131      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38132      */
38133     maxText : "The maximum value for this field is {0}",
38134     /**
38135      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38136      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38137      */
38138     nanText : "{0} is not a valid number",
38139
38140     // private
38141     initEvents : function(){
38142         Roo.form.NumberField.superclass.initEvents.call(this);
38143         var allowed = "0123456789";
38144         if(this.allowDecimals){
38145             allowed += this.decimalSeparator;
38146         }
38147         if(this.allowNegative){
38148             allowed += "-";
38149         }
38150         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38151         var keyPress = function(e){
38152             var k = e.getKey();
38153             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38154                 return;
38155             }
38156             var c = e.getCharCode();
38157             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38158                 e.stopEvent();
38159             }
38160         };
38161         this.el.on("keypress", keyPress, this);
38162     },
38163
38164     // private
38165     validateValue : function(value){
38166         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38167             return false;
38168         }
38169         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38170              return true;
38171         }
38172         var num = this.parseValue(value);
38173         if(isNaN(num)){
38174             this.markInvalid(String.format(this.nanText, value));
38175             return false;
38176         }
38177         if(num < this.minValue){
38178             this.markInvalid(String.format(this.minText, this.minValue));
38179             return false;
38180         }
38181         if(num > this.maxValue){
38182             this.markInvalid(String.format(this.maxText, this.maxValue));
38183             return false;
38184         }
38185         return true;
38186     },
38187
38188     getValue : function(){
38189         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38190     },
38191
38192     // private
38193     parseValue : function(value){
38194         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38195         return isNaN(value) ? '' : value;
38196     },
38197
38198     // private
38199     fixPrecision : function(value){
38200         var nan = isNaN(value);
38201         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38202             return nan ? '' : value;
38203         }
38204         return parseFloat(value).toFixed(this.decimalPrecision);
38205     },
38206
38207     setValue : function(v){
38208         v = this.fixPrecision(v);
38209         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38210     },
38211
38212     // private
38213     decimalPrecisionFcn : function(v){
38214         return Math.floor(v);
38215     },
38216
38217     beforeBlur : function(){
38218         var v = this.parseValue(this.getRawValue());
38219         if(v){
38220             this.setValue(v);
38221         }
38222     }
38223 });/*
38224  * Based on:
38225  * Ext JS Library 1.1.1
38226  * Copyright(c) 2006-2007, Ext JS, LLC.
38227  *
38228  * Originally Released Under LGPL - original licence link has changed is not relivant.
38229  *
38230  * Fork - LGPL
38231  * <script type="text/javascript">
38232  */
38233  
38234 /**
38235  * @class Roo.form.DateField
38236  * @extends Roo.form.TriggerField
38237  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38238 * @constructor
38239 * Create a new DateField
38240 * @param {Object} config
38241  */
38242 Roo.form.DateField = function(config){
38243     Roo.form.DateField.superclass.constructor.call(this, config);
38244     
38245       this.addEvents({
38246          
38247         /**
38248          * @event select
38249          * Fires when a date is selected
38250              * @param {Roo.form.DateField} combo This combo box
38251              * @param {Date} date The date selected
38252              */
38253         'select' : true
38254          
38255     });
38256     
38257     
38258     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38259     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38260     this.ddMatch = null;
38261     if(this.disabledDates){
38262         var dd = this.disabledDates;
38263         var re = "(?:";
38264         for(var i = 0; i < dd.length; i++){
38265             re += dd[i];
38266             if(i != dd.length-1) re += "|";
38267         }
38268         this.ddMatch = new RegExp(re + ")");
38269     }
38270 };
38271
38272 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38273     /**
38274      * @cfg {String} format
38275      * The default date format string which can be overriden for localization support.  The format must be
38276      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38277      */
38278     format : "m/d/y",
38279     /**
38280      * @cfg {String} altFormats
38281      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38282      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38283      */
38284     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38285     /**
38286      * @cfg {Array} disabledDays
38287      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38288      */
38289     disabledDays : null,
38290     /**
38291      * @cfg {String} disabledDaysText
38292      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38293      */
38294     disabledDaysText : "Disabled",
38295     /**
38296      * @cfg {Array} disabledDates
38297      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38298      * expression so they are very powerful. Some examples:
38299      * <ul>
38300      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38301      * <li>["03/08", "09/16"] would disable those days for every year</li>
38302      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38303      * <li>["03/../2006"] would disable every day in March 2006</li>
38304      * <li>["^03"] would disable every day in every March</li>
38305      * </ul>
38306      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38307      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38308      */
38309     disabledDates : null,
38310     /**
38311      * @cfg {String} disabledDatesText
38312      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38313      */
38314     disabledDatesText : "Disabled",
38315     /**
38316      * @cfg {Date/String} minValue
38317      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38318      * valid format (defaults to null).
38319      */
38320     minValue : null,
38321     /**
38322      * @cfg {Date/String} maxValue
38323      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38324      * valid format (defaults to null).
38325      */
38326     maxValue : null,
38327     /**
38328      * @cfg {String} minText
38329      * The error text to display when the date in the cell is before minValue (defaults to
38330      * 'The date in this field must be after {minValue}').
38331      */
38332     minText : "The date in this field must be equal to or after {0}",
38333     /**
38334      * @cfg {String} maxText
38335      * The error text to display when the date in the cell is after maxValue (defaults to
38336      * 'The date in this field must be before {maxValue}').
38337      */
38338     maxText : "The date in this field must be equal to or before {0}",
38339     /**
38340      * @cfg {String} invalidText
38341      * The error text to display when the date in the field is invalid (defaults to
38342      * '{value} is not a valid date - it must be in the format {format}').
38343      */
38344     invalidText : "{0} is not a valid date - it must be in the format {1}",
38345     /**
38346      * @cfg {String} triggerClass
38347      * An additional CSS class used to style the trigger button.  The trigger will always get the
38348      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38349      * which displays a calendar icon).
38350      */
38351     triggerClass : 'x-form-date-trigger',
38352     
38353
38354     /**
38355      * @cfg {Boolean} useIso
38356      * if enabled, then the date field will use a hidden field to store the 
38357      * real value as iso formated date. default (false)
38358      */ 
38359     useIso : false,
38360     /**
38361      * @cfg {String/Object} autoCreate
38362      * A DomHelper element spec, or true for a default element spec (defaults to
38363      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38364      */ 
38365     // private
38366     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38367     
38368     // private
38369     hiddenField: false,
38370     
38371     onRender : function(ct, position)
38372     {
38373         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38374         if (this.useIso) {
38375             //this.el.dom.removeAttribute('name'); 
38376             Roo.log("Changing name?");
38377             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38378             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38379                     'before', true);
38380             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38381             // prevent input submission
38382             this.hiddenName = this.name;
38383         }
38384             
38385             
38386     },
38387     
38388     // private
38389     validateValue : function(value)
38390     {
38391         value = this.formatDate(value);
38392         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38393             Roo.log('super failed');
38394             return false;
38395         }
38396         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38397              return true;
38398         }
38399         var svalue = value;
38400         value = this.parseDate(value);
38401         if(!value){
38402             Roo.log('parse date failed' + svalue);
38403             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38404             return false;
38405         }
38406         var time = value.getTime();
38407         if(this.minValue && time < this.minValue.getTime()){
38408             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38409             return false;
38410         }
38411         if(this.maxValue && time > this.maxValue.getTime()){
38412             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38413             return false;
38414         }
38415         if(this.disabledDays){
38416             var day = value.getDay();
38417             for(var i = 0; i < this.disabledDays.length; i++) {
38418                 if(day === this.disabledDays[i]){
38419                     this.markInvalid(this.disabledDaysText);
38420                     return false;
38421                 }
38422             }
38423         }
38424         var fvalue = this.formatDate(value);
38425         if(this.ddMatch && this.ddMatch.test(fvalue)){
38426             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38427             return false;
38428         }
38429         return true;
38430     },
38431
38432     // private
38433     // Provides logic to override the default TriggerField.validateBlur which just returns true
38434     validateBlur : function(){
38435         return !this.menu || !this.menu.isVisible();
38436     },
38437     
38438     getName: function()
38439     {
38440         // returns hidden if it's set..
38441         if (!this.rendered) {return ''};
38442         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38443         
38444     },
38445
38446     /**
38447      * Returns the current date value of the date field.
38448      * @return {Date} The date value
38449      */
38450     getValue : function(){
38451         
38452         return  this.hiddenField ?
38453                 this.hiddenField.value :
38454                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38455     },
38456
38457     /**
38458      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38459      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38460      * (the default format used is "m/d/y").
38461      * <br />Usage:
38462      * <pre><code>
38463 //All of these calls set the same date value (May 4, 2006)
38464
38465 //Pass a date object:
38466 var dt = new Date('5/4/06');
38467 dateField.setValue(dt);
38468
38469 //Pass a date string (default format):
38470 dateField.setValue('5/4/06');
38471
38472 //Pass a date string (custom format):
38473 dateField.format = 'Y-m-d';
38474 dateField.setValue('2006-5-4');
38475 </code></pre>
38476      * @param {String/Date} date The date or valid date string
38477      */
38478     setValue : function(date){
38479         if (this.hiddenField) {
38480             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38481         }
38482         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38483         // make sure the value field is always stored as a date..
38484         this.value = this.parseDate(date);
38485         
38486         
38487     },
38488
38489     // private
38490     parseDate : function(value){
38491         if(!value || value instanceof Date){
38492             return value;
38493         }
38494         var v = Date.parseDate(value, this.format);
38495          if (!v && this.useIso) {
38496             v = Date.parseDate(value, 'Y-m-d');
38497         }
38498         if(!v && this.altFormats){
38499             if(!this.altFormatsArray){
38500                 this.altFormatsArray = this.altFormats.split("|");
38501             }
38502             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38503                 v = Date.parseDate(value, this.altFormatsArray[i]);
38504             }
38505         }
38506         return v;
38507     },
38508
38509     // private
38510     formatDate : function(date, fmt){
38511         return (!date || !(date instanceof Date)) ?
38512                date : date.dateFormat(fmt || this.format);
38513     },
38514
38515     // private
38516     menuListeners : {
38517         select: function(m, d){
38518             
38519             this.setValue(d);
38520             this.fireEvent('select', this, d);
38521         },
38522         show : function(){ // retain focus styling
38523             this.onFocus();
38524         },
38525         hide : function(){
38526             this.focus.defer(10, this);
38527             var ml = this.menuListeners;
38528             this.menu.un("select", ml.select,  this);
38529             this.menu.un("show", ml.show,  this);
38530             this.menu.un("hide", ml.hide,  this);
38531         }
38532     },
38533
38534     // private
38535     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38536     onTriggerClick : function(){
38537         if(this.disabled){
38538             return;
38539         }
38540         if(this.menu == null){
38541             this.menu = new Roo.menu.DateMenu();
38542         }
38543         Roo.apply(this.menu.picker,  {
38544             showClear: this.allowBlank,
38545             minDate : this.minValue,
38546             maxDate : this.maxValue,
38547             disabledDatesRE : this.ddMatch,
38548             disabledDatesText : this.disabledDatesText,
38549             disabledDays : this.disabledDays,
38550             disabledDaysText : this.disabledDaysText,
38551             format : this.useIso ? 'Y-m-d' : this.format,
38552             minText : String.format(this.minText, this.formatDate(this.minValue)),
38553             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38554         });
38555         this.menu.on(Roo.apply({}, this.menuListeners, {
38556             scope:this
38557         }));
38558         this.menu.picker.setValue(this.getValue() || new Date());
38559         this.menu.show(this.el, "tl-bl?");
38560     },
38561
38562     beforeBlur : function(){
38563         var v = this.parseDate(this.getRawValue());
38564         if(v){
38565             this.setValue(v);
38566         }
38567     },
38568
38569     /*@
38570      * overide
38571      * 
38572      */
38573     isDirty : function() {
38574         if(this.disabled) {
38575             return false;
38576         }
38577         
38578         if(typeof(this.startValue) === 'undefined'){
38579             return false;
38580         }
38581         
38582         return String(this.getValue()) !== String(this.startValue);
38583         
38584     }
38585 });/*
38586  * Based on:
38587  * Ext JS Library 1.1.1
38588  * Copyright(c) 2006-2007, Ext JS, LLC.
38589  *
38590  * Originally Released Under LGPL - original licence link has changed is not relivant.
38591  *
38592  * Fork - LGPL
38593  * <script type="text/javascript">
38594  */
38595  
38596 /**
38597  * @class Roo.form.MonthField
38598  * @extends Roo.form.TriggerField
38599  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38600 * @constructor
38601 * Create a new MonthField
38602 * @param {Object} config
38603  */
38604 Roo.form.MonthField = function(config){
38605     
38606     Roo.form.MonthField.superclass.constructor.call(this, config);
38607     
38608       this.addEvents({
38609          
38610         /**
38611          * @event select
38612          * Fires when a date is selected
38613              * @param {Roo.form.MonthFieeld} combo This combo box
38614              * @param {Date} date The date selected
38615              */
38616         'select' : true
38617          
38618     });
38619     
38620     
38621     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38622     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38623     this.ddMatch = null;
38624     if(this.disabledDates){
38625         var dd = this.disabledDates;
38626         var re = "(?:";
38627         for(var i = 0; i < dd.length; i++){
38628             re += dd[i];
38629             if(i != dd.length-1) re += "|";
38630         }
38631         this.ddMatch = new RegExp(re + ")");
38632     }
38633 };
38634
38635 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38636     /**
38637      * @cfg {String} format
38638      * The default date format string which can be overriden for localization support.  The format must be
38639      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38640      */
38641     format : "M Y",
38642     /**
38643      * @cfg {String} altFormats
38644      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38645      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38646      */
38647     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38648     /**
38649      * @cfg {Array} disabledDays
38650      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38651      */
38652     disabledDays : [0,1,2,3,4,5,6],
38653     /**
38654      * @cfg {String} disabledDaysText
38655      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38656      */
38657     disabledDaysText : "Disabled",
38658     /**
38659      * @cfg {Array} disabledDates
38660      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38661      * expression so they are very powerful. Some examples:
38662      * <ul>
38663      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38664      * <li>["03/08", "09/16"] would disable those days for every year</li>
38665      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38666      * <li>["03/../2006"] would disable every day in March 2006</li>
38667      * <li>["^03"] would disable every day in every March</li>
38668      * </ul>
38669      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38670      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38671      */
38672     disabledDates : null,
38673     /**
38674      * @cfg {String} disabledDatesText
38675      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38676      */
38677     disabledDatesText : "Disabled",
38678     /**
38679      * @cfg {Date/String} minValue
38680      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38681      * valid format (defaults to null).
38682      */
38683     minValue : null,
38684     /**
38685      * @cfg {Date/String} maxValue
38686      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38687      * valid format (defaults to null).
38688      */
38689     maxValue : null,
38690     /**
38691      * @cfg {String} minText
38692      * The error text to display when the date in the cell is before minValue (defaults to
38693      * 'The date in this field must be after {minValue}').
38694      */
38695     minText : "The date in this field must be equal to or after {0}",
38696     /**
38697      * @cfg {String} maxTextf
38698      * The error text to display when the date in the cell is after maxValue (defaults to
38699      * 'The date in this field must be before {maxValue}').
38700      */
38701     maxText : "The date in this field must be equal to or before {0}",
38702     /**
38703      * @cfg {String} invalidText
38704      * The error text to display when the date in the field is invalid (defaults to
38705      * '{value} is not a valid date - it must be in the format {format}').
38706      */
38707     invalidText : "{0} is not a valid date - it must be in the format {1}",
38708     /**
38709      * @cfg {String} triggerClass
38710      * An additional CSS class used to style the trigger button.  The trigger will always get the
38711      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38712      * which displays a calendar icon).
38713      */
38714     triggerClass : 'x-form-date-trigger',
38715     
38716
38717     /**
38718      * @cfg {Boolean} useIso
38719      * if enabled, then the date field will use a hidden field to store the 
38720      * real value as iso formated date. default (true)
38721      */ 
38722     useIso : true,
38723     /**
38724      * @cfg {String/Object} autoCreate
38725      * A DomHelper element spec, or true for a default element spec (defaults to
38726      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38727      */ 
38728     // private
38729     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38730     
38731     // private
38732     hiddenField: false,
38733     
38734     hideMonthPicker : false,
38735     
38736     onRender : function(ct, position)
38737     {
38738         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38739         if (this.useIso) {
38740             this.el.dom.removeAttribute('name'); 
38741             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38742                     'before', true);
38743             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38744             // prevent input submission
38745             this.hiddenName = this.name;
38746         }
38747             
38748             
38749     },
38750     
38751     // private
38752     validateValue : function(value)
38753     {
38754         value = this.formatDate(value);
38755         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38756             return false;
38757         }
38758         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38759              return true;
38760         }
38761         var svalue = value;
38762         value = this.parseDate(value);
38763         if(!value){
38764             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38765             return false;
38766         }
38767         var time = value.getTime();
38768         if(this.minValue && time < this.minValue.getTime()){
38769             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38770             return false;
38771         }
38772         if(this.maxValue && time > this.maxValue.getTime()){
38773             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38774             return false;
38775         }
38776         /*if(this.disabledDays){
38777             var day = value.getDay();
38778             for(var i = 0; i < this.disabledDays.length; i++) {
38779                 if(day === this.disabledDays[i]){
38780                     this.markInvalid(this.disabledDaysText);
38781                     return false;
38782                 }
38783             }
38784         }
38785         */
38786         var fvalue = this.formatDate(value);
38787         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38788             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38789             return false;
38790         }
38791         */
38792         return true;
38793     },
38794
38795     // private
38796     // Provides logic to override the default TriggerField.validateBlur which just returns true
38797     validateBlur : function(){
38798         return !this.menu || !this.menu.isVisible();
38799     },
38800
38801     /**
38802      * Returns the current date value of the date field.
38803      * @return {Date} The date value
38804      */
38805     getValue : function(){
38806         
38807         
38808         
38809         return  this.hiddenField ?
38810                 this.hiddenField.value :
38811                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38812     },
38813
38814     /**
38815      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38816      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38817      * (the default format used is "m/d/y").
38818      * <br />Usage:
38819      * <pre><code>
38820 //All of these calls set the same date value (May 4, 2006)
38821
38822 //Pass a date object:
38823 var dt = new Date('5/4/06');
38824 monthField.setValue(dt);
38825
38826 //Pass a date string (default format):
38827 monthField.setValue('5/4/06');
38828
38829 //Pass a date string (custom format):
38830 monthField.format = 'Y-m-d';
38831 monthField.setValue('2006-5-4');
38832 </code></pre>
38833      * @param {String/Date} date The date or valid date string
38834      */
38835     setValue : function(date){
38836         Roo.log('month setValue' + date);
38837         // can only be first of month..
38838         
38839         var val = this.parseDate(date);
38840         
38841         if (this.hiddenField) {
38842             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38843         }
38844         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38845         this.value = this.parseDate(date);
38846     },
38847
38848     // private
38849     parseDate : function(value){
38850         if(!value || value instanceof Date){
38851             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38852             return value;
38853         }
38854         var v = Date.parseDate(value, this.format);
38855         if (!v && this.useIso) {
38856             v = Date.parseDate(value, 'Y-m-d');
38857         }
38858         if (v) {
38859             // 
38860             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38861         }
38862         
38863         
38864         if(!v && this.altFormats){
38865             if(!this.altFormatsArray){
38866                 this.altFormatsArray = this.altFormats.split("|");
38867             }
38868             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38869                 v = Date.parseDate(value, this.altFormatsArray[i]);
38870             }
38871         }
38872         return v;
38873     },
38874
38875     // private
38876     formatDate : function(date, fmt){
38877         return (!date || !(date instanceof Date)) ?
38878                date : date.dateFormat(fmt || this.format);
38879     },
38880
38881     // private
38882     menuListeners : {
38883         select: function(m, d){
38884             this.setValue(d);
38885             this.fireEvent('select', this, d);
38886         },
38887         show : function(){ // retain focus styling
38888             this.onFocus();
38889         },
38890         hide : function(){
38891             this.focus.defer(10, this);
38892             var ml = this.menuListeners;
38893             this.menu.un("select", ml.select,  this);
38894             this.menu.un("show", ml.show,  this);
38895             this.menu.un("hide", ml.hide,  this);
38896         }
38897     },
38898     // private
38899     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38900     onTriggerClick : function(){
38901         if(this.disabled){
38902             return;
38903         }
38904         if(this.menu == null){
38905             this.menu = new Roo.menu.DateMenu();
38906            
38907         }
38908         
38909         Roo.apply(this.menu.picker,  {
38910             
38911             showClear: this.allowBlank,
38912             minDate : this.minValue,
38913             maxDate : this.maxValue,
38914             disabledDatesRE : this.ddMatch,
38915             disabledDatesText : this.disabledDatesText,
38916             
38917             format : this.useIso ? 'Y-m-d' : this.format,
38918             minText : String.format(this.minText, this.formatDate(this.minValue)),
38919             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38920             
38921         });
38922          this.menu.on(Roo.apply({}, this.menuListeners, {
38923             scope:this
38924         }));
38925        
38926         
38927         var m = this.menu;
38928         var p = m.picker;
38929         
38930         // hide month picker get's called when we called by 'before hide';
38931         
38932         var ignorehide = true;
38933         p.hideMonthPicker  = function(disableAnim){
38934             if (ignorehide) {
38935                 return;
38936             }
38937              if(this.monthPicker){
38938                 Roo.log("hideMonthPicker called");
38939                 if(disableAnim === true){
38940                     this.monthPicker.hide();
38941                 }else{
38942                     this.monthPicker.slideOut('t', {duration:.2});
38943                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38944                     p.fireEvent("select", this, this.value);
38945                     m.hide();
38946                 }
38947             }
38948         }
38949         
38950         Roo.log('picker set value');
38951         Roo.log(this.getValue());
38952         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38953         m.show(this.el, 'tl-bl?');
38954         ignorehide  = false;
38955         // this will trigger hideMonthPicker..
38956         
38957         
38958         // hidden the day picker
38959         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38960         
38961         
38962         
38963       
38964         
38965         p.showMonthPicker.defer(100, p);
38966     
38967         
38968        
38969     },
38970
38971     beforeBlur : function(){
38972         var v = this.parseDate(this.getRawValue());
38973         if(v){
38974             this.setValue(v);
38975         }
38976     }
38977
38978     /** @cfg {Boolean} grow @hide */
38979     /** @cfg {Number} growMin @hide */
38980     /** @cfg {Number} growMax @hide */
38981     /**
38982      * @hide
38983      * @method autoSize
38984      */
38985 });/*
38986  * Based on:
38987  * Ext JS Library 1.1.1
38988  * Copyright(c) 2006-2007, Ext JS, LLC.
38989  *
38990  * Originally Released Under LGPL - original licence link has changed is not relivant.
38991  *
38992  * Fork - LGPL
38993  * <script type="text/javascript">
38994  */
38995  
38996
38997 /**
38998  * @class Roo.form.ComboBox
38999  * @extends Roo.form.TriggerField
39000  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39001  * @constructor
39002  * Create a new ComboBox.
39003  * @param {Object} config Configuration options
39004  */
39005 Roo.form.ComboBox = function(config){
39006     Roo.form.ComboBox.superclass.constructor.call(this, config);
39007     this.addEvents({
39008         /**
39009          * @event expand
39010          * Fires when the dropdown list is expanded
39011              * @param {Roo.form.ComboBox} combo This combo box
39012              */
39013         'expand' : true,
39014         /**
39015          * @event collapse
39016          * Fires when the dropdown list is collapsed
39017              * @param {Roo.form.ComboBox} combo This combo box
39018              */
39019         'collapse' : true,
39020         /**
39021          * @event beforeselect
39022          * Fires before a list item is selected. Return false to cancel the selection.
39023              * @param {Roo.form.ComboBox} combo This combo box
39024              * @param {Roo.data.Record} record The data record returned from the underlying store
39025              * @param {Number} index The index of the selected item in the dropdown list
39026              */
39027         'beforeselect' : true,
39028         /**
39029          * @event select
39030          * Fires when a list item is selected
39031              * @param {Roo.form.ComboBox} combo This combo box
39032              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39033              * @param {Number} index The index of the selected item in the dropdown list
39034              */
39035         'select' : true,
39036         /**
39037          * @event beforequery
39038          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39039          * The event object passed has these properties:
39040              * @param {Roo.form.ComboBox} combo This combo box
39041              * @param {String} query The query
39042              * @param {Boolean} forceAll true to force "all" query
39043              * @param {Boolean} cancel true to cancel the query
39044              * @param {Object} e The query event object
39045              */
39046         'beforequery': true,
39047          /**
39048          * @event add
39049          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39050              * @param {Roo.form.ComboBox} combo This combo box
39051              */
39052         'add' : true,
39053         /**
39054          * @event edit
39055          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39056              * @param {Roo.form.ComboBox} combo This combo box
39057              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39058              */
39059         'edit' : true
39060         
39061         
39062     });
39063     if(this.transform){
39064         this.allowDomMove = false;
39065         var s = Roo.getDom(this.transform);
39066         if(!this.hiddenName){
39067             this.hiddenName = s.name;
39068         }
39069         if(!this.store){
39070             this.mode = 'local';
39071             var d = [], opts = s.options;
39072             for(var i = 0, len = opts.length;i < len; i++){
39073                 var o = opts[i];
39074                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39075                 if(o.selected) {
39076                     this.value = value;
39077                 }
39078                 d.push([value, o.text]);
39079             }
39080             this.store = new Roo.data.SimpleStore({
39081                 'id': 0,
39082                 fields: ['value', 'text'],
39083                 data : d
39084             });
39085             this.valueField = 'value';
39086             this.displayField = 'text';
39087         }
39088         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39089         if(!this.lazyRender){
39090             this.target = true;
39091             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39092             s.parentNode.removeChild(s); // remove it
39093             this.render(this.el.parentNode);
39094         }else{
39095             s.parentNode.removeChild(s); // remove it
39096         }
39097
39098     }
39099     if (this.store) {
39100         this.store = Roo.factory(this.store, Roo.data);
39101     }
39102     
39103     this.selectedIndex = -1;
39104     if(this.mode == 'local'){
39105         if(config.queryDelay === undefined){
39106             this.queryDelay = 10;
39107         }
39108         if(config.minChars === undefined){
39109             this.minChars = 0;
39110         }
39111     }
39112 };
39113
39114 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39115     /**
39116      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39117      */
39118     /**
39119      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39120      * rendering into an Roo.Editor, defaults to false)
39121      */
39122     /**
39123      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39124      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39125      */
39126     /**
39127      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39128      */
39129     /**
39130      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39131      * the dropdown list (defaults to undefined, with no header element)
39132      */
39133
39134      /**
39135      * @cfg {String/Roo.Template} tpl The template to use to render the output
39136      */
39137      
39138     // private
39139     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39140     /**
39141      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39142      */
39143     listWidth: undefined,
39144     /**
39145      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39146      * mode = 'remote' or 'text' if mode = 'local')
39147      */
39148     displayField: undefined,
39149     /**
39150      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39151      * mode = 'remote' or 'value' if mode = 'local'). 
39152      * Note: use of a valueField requires the user make a selection
39153      * in order for a value to be mapped.
39154      */
39155     valueField: undefined,
39156     
39157     
39158     /**
39159      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39160      * field's data value (defaults to the underlying DOM element's name)
39161      */
39162     hiddenName: undefined,
39163     /**
39164      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39165      */
39166     listClass: '',
39167     /**
39168      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39169      */
39170     selectedClass: 'x-combo-selected',
39171     /**
39172      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39173      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39174      * which displays a downward arrow icon).
39175      */
39176     triggerClass : 'x-form-arrow-trigger',
39177     /**
39178      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39179      */
39180     shadow:'sides',
39181     /**
39182      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39183      * anchor positions (defaults to 'tl-bl')
39184      */
39185     listAlign: 'tl-bl?',
39186     /**
39187      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39188      */
39189     maxHeight: 300,
39190     /**
39191      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39192      * query specified by the allQuery config option (defaults to 'query')
39193      */
39194     triggerAction: 'query',
39195     /**
39196      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39197      * (defaults to 4, does not apply if editable = false)
39198      */
39199     minChars : 4,
39200     /**
39201      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39202      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39203      */
39204     typeAhead: false,
39205     /**
39206      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39207      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39208      */
39209     queryDelay: 500,
39210     /**
39211      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39212      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39213      */
39214     pageSize: 0,
39215     /**
39216      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39217      * when editable = true (defaults to false)
39218      */
39219     selectOnFocus:false,
39220     /**
39221      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39222      */
39223     queryParam: 'query',
39224     /**
39225      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39226      * when mode = 'remote' (defaults to 'Loading...')
39227      */
39228     loadingText: 'Loading...',
39229     /**
39230      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39231      */
39232     resizable: false,
39233     /**
39234      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39235      */
39236     handleHeight : 8,
39237     /**
39238      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39239      * traditional select (defaults to true)
39240      */
39241     editable: true,
39242     /**
39243      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39244      */
39245     allQuery: '',
39246     /**
39247      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39248      */
39249     mode: 'remote',
39250     /**
39251      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39252      * listWidth has a higher value)
39253      */
39254     minListWidth : 70,
39255     /**
39256      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39257      * allow the user to set arbitrary text into the field (defaults to false)
39258      */
39259     forceSelection:false,
39260     /**
39261      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39262      * if typeAhead = true (defaults to 250)
39263      */
39264     typeAheadDelay : 250,
39265     /**
39266      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39267      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39268      */
39269     valueNotFoundText : undefined,
39270     /**
39271      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39272      */
39273     blockFocus : false,
39274     
39275     /**
39276      * @cfg {Boolean} disableClear Disable showing of clear button.
39277      */
39278     disableClear : false,
39279     /**
39280      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39281      */
39282     alwaysQuery : false,
39283     
39284     //private
39285     addicon : false,
39286     editicon: false,
39287     
39288     // element that contains real text value.. (when hidden is used..)
39289      
39290     // private
39291     onRender : function(ct, position){
39292         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39293         if(this.hiddenName){
39294             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39295                     'before', true);
39296             this.hiddenField.value =
39297                 this.hiddenValue !== undefined ? this.hiddenValue :
39298                 this.value !== undefined ? this.value : '';
39299
39300             // prevent input submission
39301             this.el.dom.removeAttribute('name');
39302              
39303              
39304         }
39305         if(Roo.isGecko){
39306             this.el.dom.setAttribute('autocomplete', 'off');
39307         }
39308
39309         var cls = 'x-combo-list';
39310
39311         this.list = new Roo.Layer({
39312             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39313         });
39314
39315         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39316         this.list.setWidth(lw);
39317         this.list.swallowEvent('mousewheel');
39318         this.assetHeight = 0;
39319
39320         if(this.title){
39321             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39322             this.assetHeight += this.header.getHeight();
39323         }
39324
39325         this.innerList = this.list.createChild({cls:cls+'-inner'});
39326         this.innerList.on('mouseover', this.onViewOver, this);
39327         this.innerList.on('mousemove', this.onViewMove, this);
39328         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39329         
39330         if(this.allowBlank && !this.pageSize && !this.disableClear){
39331             this.footer = this.list.createChild({cls:cls+'-ft'});
39332             this.pageTb = new Roo.Toolbar(this.footer);
39333            
39334         }
39335         if(this.pageSize){
39336             this.footer = this.list.createChild({cls:cls+'-ft'});
39337             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39338                     {pageSize: this.pageSize});
39339             
39340         }
39341         
39342         if (this.pageTb && this.allowBlank && !this.disableClear) {
39343             var _this = this;
39344             this.pageTb.add(new Roo.Toolbar.Fill(), {
39345                 cls: 'x-btn-icon x-btn-clear',
39346                 text: '&#160;',
39347                 handler: function()
39348                 {
39349                     _this.collapse();
39350                     _this.clearValue();
39351                     _this.onSelect(false, -1);
39352                 }
39353             });
39354         }
39355         if (this.footer) {
39356             this.assetHeight += this.footer.getHeight();
39357         }
39358         
39359
39360         if(!this.tpl){
39361             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39362         }
39363
39364         this.view = new Roo.View(this.innerList, this.tpl, {
39365             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39366         });
39367
39368         this.view.on('click', this.onViewClick, this);
39369
39370         this.store.on('beforeload', this.onBeforeLoad, this);
39371         this.store.on('load', this.onLoad, this);
39372         this.store.on('loadexception', this.onLoadException, this);
39373
39374         if(this.resizable){
39375             this.resizer = new Roo.Resizable(this.list,  {
39376                pinned:true, handles:'se'
39377             });
39378             this.resizer.on('resize', function(r, w, h){
39379                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39380                 this.listWidth = w;
39381                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39382                 this.restrictHeight();
39383             }, this);
39384             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39385         }
39386         if(!this.editable){
39387             this.editable = true;
39388             this.setEditable(false);
39389         }  
39390         
39391         
39392         if (typeof(this.events.add.listeners) != 'undefined') {
39393             
39394             this.addicon = this.wrap.createChild(
39395                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39396        
39397             this.addicon.on('click', function(e) {
39398                 this.fireEvent('add', this);
39399             }, this);
39400         }
39401         if (typeof(this.events.edit.listeners) != 'undefined') {
39402             
39403             this.editicon = this.wrap.createChild(
39404                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39405             if (this.addicon) {
39406                 this.editicon.setStyle('margin-left', '40px');
39407             }
39408             this.editicon.on('click', function(e) {
39409                 
39410                 // we fire even  if inothing is selected..
39411                 this.fireEvent('edit', this, this.lastData );
39412                 
39413             }, this);
39414         }
39415         
39416         
39417         
39418     },
39419
39420     // private
39421     initEvents : function(){
39422         Roo.form.ComboBox.superclass.initEvents.call(this);
39423
39424         this.keyNav = new Roo.KeyNav(this.el, {
39425             "up" : function(e){
39426                 this.inKeyMode = true;
39427                 this.selectPrev();
39428             },
39429
39430             "down" : function(e){
39431                 if(!this.isExpanded()){
39432                     this.onTriggerClick();
39433                 }else{
39434                     this.inKeyMode = true;
39435                     this.selectNext();
39436                 }
39437             },
39438
39439             "enter" : function(e){
39440                 this.onViewClick();
39441                 //return true;
39442             },
39443
39444             "esc" : function(e){
39445                 this.collapse();
39446             },
39447
39448             "tab" : function(e){
39449                 this.onViewClick(false);
39450                 this.fireEvent("specialkey", this, e);
39451                 return true;
39452             },
39453
39454             scope : this,
39455
39456             doRelay : function(foo, bar, hname){
39457                 if(hname == 'down' || this.scope.isExpanded()){
39458                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39459                 }
39460                 return true;
39461             },
39462
39463             forceKeyDown: true
39464         });
39465         this.queryDelay = Math.max(this.queryDelay || 10,
39466                 this.mode == 'local' ? 10 : 250);
39467         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39468         if(this.typeAhead){
39469             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39470         }
39471         if(this.editable !== false){
39472             this.el.on("keyup", this.onKeyUp, this);
39473         }
39474         if(this.forceSelection){
39475             this.on('blur', this.doForce, this);
39476         }
39477     },
39478
39479     onDestroy : function(){
39480         if(this.view){
39481             this.view.setStore(null);
39482             this.view.el.removeAllListeners();
39483             this.view.el.remove();
39484             this.view.purgeListeners();
39485         }
39486         if(this.list){
39487             this.list.destroy();
39488         }
39489         if(this.store){
39490             this.store.un('beforeload', this.onBeforeLoad, this);
39491             this.store.un('load', this.onLoad, this);
39492             this.store.un('loadexception', this.onLoadException, this);
39493         }
39494         Roo.form.ComboBox.superclass.onDestroy.call(this);
39495     },
39496
39497     // private
39498     fireKey : function(e){
39499         if(e.isNavKeyPress() && !this.list.isVisible()){
39500             this.fireEvent("specialkey", this, e);
39501         }
39502     },
39503
39504     // private
39505     onResize: function(w, h){
39506         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39507         
39508         if(typeof w != 'number'){
39509             // we do not handle it!?!?
39510             return;
39511         }
39512         var tw = this.trigger.getWidth();
39513         tw += this.addicon ? this.addicon.getWidth() : 0;
39514         tw += this.editicon ? this.editicon.getWidth() : 0;
39515         var x = w - tw;
39516         this.el.setWidth( this.adjustWidth('input', x));
39517             
39518         this.trigger.setStyle('left', x+'px');
39519         
39520         if(this.list && this.listWidth === undefined){
39521             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39522             this.list.setWidth(lw);
39523             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39524         }
39525         
39526     
39527         
39528     },
39529
39530     /**
39531      * Allow or prevent the user from directly editing the field text.  If false is passed,
39532      * the user will only be able to select from the items defined in the dropdown list.  This method
39533      * is the runtime equivalent of setting the 'editable' config option at config time.
39534      * @param {Boolean} value True to allow the user to directly edit the field text
39535      */
39536     setEditable : function(value){
39537         if(value == this.editable){
39538             return;
39539         }
39540         this.editable = value;
39541         if(!value){
39542             this.el.dom.setAttribute('readOnly', true);
39543             this.el.on('mousedown', this.onTriggerClick,  this);
39544             this.el.addClass('x-combo-noedit');
39545         }else{
39546             this.el.dom.setAttribute('readOnly', false);
39547             this.el.un('mousedown', this.onTriggerClick,  this);
39548             this.el.removeClass('x-combo-noedit');
39549         }
39550     },
39551
39552     // private
39553     onBeforeLoad : function(){
39554         if(!this.hasFocus){
39555             return;
39556         }
39557         this.innerList.update(this.loadingText ?
39558                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39559         this.restrictHeight();
39560         this.selectedIndex = -1;
39561     },
39562
39563     // private
39564     onLoad : function(){
39565         if(!this.hasFocus){
39566             return;
39567         }
39568         if(this.store.getCount() > 0){
39569             this.expand();
39570             this.restrictHeight();
39571             if(this.lastQuery == this.allQuery){
39572                 if(this.editable){
39573                     this.el.dom.select();
39574                 }
39575                 if(!this.selectByValue(this.value, true)){
39576                     this.select(0, true);
39577                 }
39578             }else{
39579                 this.selectNext();
39580                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39581                     this.taTask.delay(this.typeAheadDelay);
39582                 }
39583             }
39584         }else{
39585             this.onEmptyResults();
39586         }
39587         //this.el.focus();
39588     },
39589     // private
39590     onLoadException : function()
39591     {
39592         this.collapse();
39593         Roo.log(this.store.reader.jsonData);
39594         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39595             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39596         }
39597         
39598         
39599     },
39600     // private
39601     onTypeAhead : function(){
39602         if(this.store.getCount() > 0){
39603             var r = this.store.getAt(0);
39604             var newValue = r.data[this.displayField];
39605             var len = newValue.length;
39606             var selStart = this.getRawValue().length;
39607             if(selStart != len){
39608                 this.setRawValue(newValue);
39609                 this.selectText(selStart, newValue.length);
39610             }
39611         }
39612     },
39613
39614     // private
39615     onSelect : function(record, index){
39616         if(this.fireEvent('beforeselect', this, record, index) !== false){
39617             this.setFromData(index > -1 ? record.data : false);
39618             this.collapse();
39619             this.fireEvent('select', this, record, index);
39620         }
39621     },
39622
39623     /**
39624      * Returns the currently selected field value or empty string if no value is set.
39625      * @return {String} value The selected value
39626      */
39627     getValue : function(){
39628         if(this.valueField){
39629             return typeof this.value != 'undefined' ? this.value : '';
39630         }else{
39631             return Roo.form.ComboBox.superclass.getValue.call(this);
39632         }
39633     },
39634
39635     /**
39636      * Clears any text/value currently set in the field
39637      */
39638     clearValue : function(){
39639         if(this.hiddenField){
39640             this.hiddenField.value = '';
39641         }
39642         this.value = '';
39643         this.setRawValue('');
39644         this.lastSelectionText = '';
39645         
39646     },
39647
39648     /**
39649      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39650      * will be displayed in the field.  If the value does not match the data value of an existing item,
39651      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39652      * Otherwise the field will be blank (although the value will still be set).
39653      * @param {String} value The value to match
39654      */
39655     setValue : function(v){
39656         var text = v;
39657         if(this.valueField){
39658             var r = this.findRecord(this.valueField, v);
39659             if(r){
39660                 text = r.data[this.displayField];
39661             }else if(this.valueNotFoundText !== undefined){
39662                 text = this.valueNotFoundText;
39663             }
39664         }
39665         this.lastSelectionText = text;
39666         if(this.hiddenField){
39667             this.hiddenField.value = v;
39668         }
39669         Roo.form.ComboBox.superclass.setValue.call(this, text);
39670         this.value = v;
39671     },
39672     /**
39673      * @property {Object} the last set data for the element
39674      */
39675     
39676     lastData : false,
39677     /**
39678      * Sets the value of the field based on a object which is related to the record format for the store.
39679      * @param {Object} value the value to set as. or false on reset?
39680      */
39681     setFromData : function(o){
39682         var dv = ''; // display value
39683         var vv = ''; // value value..
39684         this.lastData = o;
39685         if (this.displayField) {
39686             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39687         } else {
39688             // this is an error condition!!!
39689             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39690         }
39691         
39692         if(this.valueField){
39693             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39694         }
39695         if(this.hiddenField){
39696             this.hiddenField.value = vv;
39697             
39698             this.lastSelectionText = dv;
39699             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39700             this.value = vv;
39701             return;
39702         }
39703         // no hidden field.. - we store the value in 'value', but still display
39704         // display field!!!!
39705         this.lastSelectionText = dv;
39706         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39707         this.value = vv;
39708         
39709         
39710     },
39711     // private
39712     reset : function(){
39713         // overridden so that last data is reset..
39714         this.setValue(this.resetValue);
39715         this.clearInvalid();
39716         this.lastData = false;
39717         if (this.view) {
39718             this.view.clearSelections();
39719         }
39720     },
39721     // private
39722     findRecord : function(prop, value){
39723         var record;
39724         if(this.store.getCount() > 0){
39725             this.store.each(function(r){
39726                 if(r.data[prop] == value){
39727                     record = r;
39728                     return false;
39729                 }
39730                 return true;
39731             });
39732         }
39733         return record;
39734     },
39735     
39736     getName: function()
39737     {
39738         // returns hidden if it's set..
39739         if (!this.rendered) {return ''};
39740         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39741         
39742     },
39743     // private
39744     onViewMove : function(e, t){
39745         this.inKeyMode = false;
39746     },
39747
39748     // private
39749     onViewOver : function(e, t){
39750         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39751             return;
39752         }
39753         var item = this.view.findItemFromChild(t);
39754         if(item){
39755             var index = this.view.indexOf(item);
39756             this.select(index, false);
39757         }
39758     },
39759
39760     // private
39761     onViewClick : function(doFocus)
39762     {
39763         var index = this.view.getSelectedIndexes()[0];
39764         var r = this.store.getAt(index);
39765         if(r){
39766             this.onSelect(r, index);
39767         }
39768         if(doFocus !== false && !this.blockFocus){
39769             this.el.focus();
39770         }
39771     },
39772
39773     // private
39774     restrictHeight : function(){
39775         this.innerList.dom.style.height = '';
39776         var inner = this.innerList.dom;
39777         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39778         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39779         this.list.beginUpdate();
39780         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39781         this.list.alignTo(this.el, this.listAlign);
39782         this.list.endUpdate();
39783     },
39784
39785     // private
39786     onEmptyResults : function(){
39787         this.collapse();
39788     },
39789
39790     /**
39791      * Returns true if the dropdown list is expanded, else false.
39792      */
39793     isExpanded : function(){
39794         return this.list.isVisible();
39795     },
39796
39797     /**
39798      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39799      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39800      * @param {String} value The data value of the item to select
39801      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39802      * selected item if it is not currently in view (defaults to true)
39803      * @return {Boolean} True if the value matched an item in the list, else false
39804      */
39805     selectByValue : function(v, scrollIntoView){
39806         if(v !== undefined && v !== null){
39807             var r = this.findRecord(this.valueField || this.displayField, v);
39808             if(r){
39809                 this.select(this.store.indexOf(r), scrollIntoView);
39810                 return true;
39811             }
39812         }
39813         return false;
39814     },
39815
39816     /**
39817      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39818      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39819      * @param {Number} index The zero-based index of the list item to select
39820      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39821      * selected item if it is not currently in view (defaults to true)
39822      */
39823     select : function(index, scrollIntoView){
39824         this.selectedIndex = index;
39825         this.view.select(index);
39826         if(scrollIntoView !== false){
39827             var el = this.view.getNode(index);
39828             if(el){
39829                 this.innerList.scrollChildIntoView(el, false);
39830             }
39831         }
39832     },
39833
39834     // private
39835     selectNext : function(){
39836         var ct = this.store.getCount();
39837         if(ct > 0){
39838             if(this.selectedIndex == -1){
39839                 this.select(0);
39840             }else if(this.selectedIndex < ct-1){
39841                 this.select(this.selectedIndex+1);
39842             }
39843         }
39844     },
39845
39846     // private
39847     selectPrev : function(){
39848         var ct = this.store.getCount();
39849         if(ct > 0){
39850             if(this.selectedIndex == -1){
39851                 this.select(0);
39852             }else if(this.selectedIndex != 0){
39853                 this.select(this.selectedIndex-1);
39854             }
39855         }
39856     },
39857
39858     // private
39859     onKeyUp : function(e){
39860         if(this.editable !== false && !e.isSpecialKey()){
39861             this.lastKey = e.getKey();
39862             this.dqTask.delay(this.queryDelay);
39863         }
39864     },
39865
39866     // private
39867     validateBlur : function(){
39868         return !this.list || !this.list.isVisible();   
39869     },
39870
39871     // private
39872     initQuery : function(){
39873         this.doQuery(this.getRawValue());
39874     },
39875
39876     // private
39877     doForce : function(){
39878         if(this.el.dom.value.length > 0){
39879             this.el.dom.value =
39880                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39881              
39882         }
39883     },
39884
39885     /**
39886      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39887      * query allowing the query action to be canceled if needed.
39888      * @param {String} query The SQL query to execute
39889      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39890      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39891      * saved in the current store (defaults to false)
39892      */
39893     doQuery : function(q, forceAll){
39894         if(q === undefined || q === null){
39895             q = '';
39896         }
39897         var qe = {
39898             query: q,
39899             forceAll: forceAll,
39900             combo: this,
39901             cancel:false
39902         };
39903         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39904             return false;
39905         }
39906         q = qe.query;
39907         forceAll = qe.forceAll;
39908         if(forceAll === true || (q.length >= this.minChars)){
39909             if(this.lastQuery != q || this.alwaysQuery){
39910                 this.lastQuery = q;
39911                 if(this.mode == 'local'){
39912                     this.selectedIndex = -1;
39913                     if(forceAll){
39914                         this.store.clearFilter();
39915                     }else{
39916                         this.store.filter(this.displayField, q);
39917                     }
39918                     this.onLoad();
39919                 }else{
39920                     this.store.baseParams[this.queryParam] = q;
39921                     this.store.load({
39922                         params: this.getParams(q)
39923                     });
39924                     this.expand();
39925                 }
39926             }else{
39927                 this.selectedIndex = -1;
39928                 this.onLoad();   
39929             }
39930         }
39931     },
39932
39933     // private
39934     getParams : function(q){
39935         var p = {};
39936         //p[this.queryParam] = q;
39937         if(this.pageSize){
39938             p.start = 0;
39939             p.limit = this.pageSize;
39940         }
39941         return p;
39942     },
39943
39944     /**
39945      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39946      */
39947     collapse : function(){
39948         if(!this.isExpanded()){
39949             return;
39950         }
39951         this.list.hide();
39952         Roo.get(document).un('mousedown', this.collapseIf, this);
39953         Roo.get(document).un('mousewheel', this.collapseIf, this);
39954         if (!this.editable) {
39955             Roo.get(document).un('keydown', this.listKeyPress, this);
39956         }
39957         this.fireEvent('collapse', this);
39958     },
39959
39960     // private
39961     collapseIf : function(e){
39962         if(!e.within(this.wrap) && !e.within(this.list)){
39963             this.collapse();
39964         }
39965     },
39966
39967     /**
39968      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39969      */
39970     expand : function(){
39971         if(this.isExpanded() || !this.hasFocus){
39972             return;
39973         }
39974         this.list.alignTo(this.el, this.listAlign);
39975         this.list.show();
39976         Roo.get(document).on('mousedown', this.collapseIf, this);
39977         Roo.get(document).on('mousewheel', this.collapseIf, this);
39978         if (!this.editable) {
39979             Roo.get(document).on('keydown', this.listKeyPress, this);
39980         }
39981         
39982         this.fireEvent('expand', this);
39983     },
39984
39985     // private
39986     // Implements the default empty TriggerField.onTriggerClick function
39987     onTriggerClick : function(){
39988         if(this.disabled){
39989             return;
39990         }
39991         if(this.isExpanded()){
39992             this.collapse();
39993             if (!this.blockFocus) {
39994                 this.el.focus();
39995             }
39996             
39997         }else {
39998             this.hasFocus = true;
39999             if(this.triggerAction == 'all') {
40000                 this.doQuery(this.allQuery, true);
40001             } else {
40002                 this.doQuery(this.getRawValue());
40003             }
40004             if (!this.blockFocus) {
40005                 this.el.focus();
40006             }
40007         }
40008     },
40009     listKeyPress : function(e)
40010     {
40011         //Roo.log('listkeypress');
40012         // scroll to first matching element based on key pres..
40013         if (e.isSpecialKey()) {
40014             return false;
40015         }
40016         var k = String.fromCharCode(e.getKey()).toUpperCase();
40017         //Roo.log(k);
40018         var match  = false;
40019         var csel = this.view.getSelectedNodes();
40020         var cselitem = false;
40021         if (csel.length) {
40022             var ix = this.view.indexOf(csel[0]);
40023             cselitem  = this.store.getAt(ix);
40024             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40025                 cselitem = false;
40026             }
40027             
40028         }
40029         
40030         this.store.each(function(v) { 
40031             if (cselitem) {
40032                 // start at existing selection.
40033                 if (cselitem.id == v.id) {
40034                     cselitem = false;
40035                 }
40036                 return;
40037             }
40038                 
40039             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40040                 match = this.store.indexOf(v);
40041                 return false;
40042             }
40043         }, this);
40044         
40045         if (match === false) {
40046             return true; // no more action?
40047         }
40048         // scroll to?
40049         this.view.select(match);
40050         var sn = Roo.get(this.view.getSelectedNodes()[0])
40051         sn.scrollIntoView(sn.dom.parentNode, false);
40052     }
40053
40054     /** 
40055     * @cfg {Boolean} grow 
40056     * @hide 
40057     */
40058     /** 
40059     * @cfg {Number} growMin 
40060     * @hide 
40061     */
40062     /** 
40063     * @cfg {Number} growMax 
40064     * @hide 
40065     */
40066     /**
40067      * @hide
40068      * @method autoSize
40069      */
40070 });/*
40071  * Copyright(c) 2010-2012, Roo J Solutions Limited
40072  *
40073  * Licence LGPL
40074  *
40075  */
40076
40077 /**
40078  * @class Roo.form.ComboBoxArray
40079  * @extends Roo.form.TextField
40080  * A facebook style adder... for lists of email / people / countries  etc...
40081  * pick multiple items from a combo box, and shows each one.
40082  *
40083  *  Fred [x]  Brian [x]  [Pick another |v]
40084  *
40085  *
40086  *  For this to work: it needs various extra information
40087  *    - normal combo problay has
40088  *      name, hiddenName
40089  *    + displayField, valueField
40090  *
40091  *    For our purpose...
40092  *
40093  *
40094  *   If we change from 'extends' to wrapping...
40095  *   
40096  *  
40097  *
40098  
40099  
40100  * @constructor
40101  * Create a new ComboBoxArray.
40102  * @param {Object} config Configuration options
40103  */
40104  
40105
40106 Roo.form.ComboBoxArray = function(config)
40107 {
40108     
40109     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40110     
40111     this.items = new Roo.util.MixedCollection(false);
40112     
40113     // construct the child combo...
40114     
40115     
40116     
40117     
40118    
40119     
40120 }
40121
40122  
40123 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40124
40125     /**
40126      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40127      */
40128     
40129     lastData : false,
40130     
40131     // behavies liek a hiddne field
40132     inputType:      'hidden',
40133     /**
40134      * @cfg {Number} width The width of the box that displays the selected element
40135      */ 
40136     width:          300,
40137
40138     
40139     
40140     /**
40141      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40142      */
40143     name : false,
40144     /**
40145      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40146      */
40147     hiddenName : false,
40148     
40149     
40150     // private the array of items that are displayed..
40151     items  : false,
40152     // private - the hidden field el.
40153     hiddenEl : false,
40154     // private - the filed el..
40155     el : false,
40156     
40157     //validateValue : function() { return true; }, // all values are ok!
40158     //onAddClick: function() { },
40159     
40160     onRender : function(ct, position) 
40161     {
40162         
40163         // create the standard hidden element
40164         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40165         
40166         
40167         // give fake names to child combo;
40168         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40169         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40170         
40171         this.combo = Roo.factory(this.combo, Roo.form);
40172         this.combo.onRender(ct, position);
40173         if (typeof(this.combo.width) != 'undefined') {
40174             this.combo.onResize(this.combo.width,0);
40175         }
40176         
40177         this.combo.initEvents();
40178         
40179         // assigned so form know we need to do this..
40180         this.store          = this.combo.store;
40181         this.valueField     = this.combo.valueField;
40182         this.displayField   = this.combo.displayField ;
40183         
40184         
40185         this.combo.wrap.addClass('x-cbarray-grp');
40186         
40187         var cbwrap = this.combo.wrap.createChild(
40188             {tag: 'div', cls: 'x-cbarray-cb'},
40189             this.combo.el.dom
40190         );
40191         
40192              
40193         this.hiddenEl = this.combo.wrap.createChild({
40194             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40195         });
40196         this.el = this.combo.wrap.createChild({
40197             tag: 'input',  type:'hidden' , name: this.name, value : ''
40198         });
40199          //   this.el.dom.removeAttribute("name");
40200         
40201         
40202         this.outerWrap = this.combo.wrap;
40203         this.wrap = cbwrap;
40204         
40205         this.outerWrap.setWidth(this.width);
40206         this.outerWrap.dom.removeChild(this.el.dom);
40207         
40208         this.wrap.dom.appendChild(this.el.dom);
40209         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40210         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40211         
40212         this.combo.trigger.setStyle('position','relative');
40213         this.combo.trigger.setStyle('left', '0px');
40214         this.combo.trigger.setStyle('top', '2px');
40215         
40216         this.combo.el.setStyle('vertical-align', 'text-bottom');
40217         
40218         //this.trigger.setStyle('vertical-align', 'top');
40219         
40220         // this should use the code from combo really... on('add' ....)
40221         if (this.adder) {
40222             
40223         
40224             this.adder = this.outerWrap.createChild(
40225                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40226             var _t = this;
40227             this.adder.on('click', function(e) {
40228                 _t.fireEvent('adderclick', this, e);
40229             }, _t);
40230         }
40231         //var _t = this;
40232         //this.adder.on('click', this.onAddClick, _t);
40233         
40234         
40235         this.combo.on('select', function(cb, rec, ix) {
40236             this.addItem(rec.data);
40237             
40238             cb.setValue('');
40239             cb.el.dom.value = '';
40240             //cb.lastData = rec.data;
40241             // add to list
40242             
40243         }, this);
40244         
40245         
40246     },
40247     
40248     
40249     getName: function()
40250     {
40251         // returns hidden if it's set..
40252         if (!this.rendered) {return ''};
40253         return  this.hiddenName ? this.hiddenName : this.name;
40254         
40255     },
40256     
40257     
40258     onResize: function(w, h){
40259         
40260         return;
40261         // not sure if this is needed..
40262         //this.combo.onResize(w,h);
40263         
40264         if(typeof w != 'number'){
40265             // we do not handle it!?!?
40266             return;
40267         }
40268         var tw = this.combo.trigger.getWidth();
40269         tw += this.addicon ? this.addicon.getWidth() : 0;
40270         tw += this.editicon ? this.editicon.getWidth() : 0;
40271         var x = w - tw;
40272         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40273             
40274         this.combo.trigger.setStyle('left', '0px');
40275         
40276         if(this.list && this.listWidth === undefined){
40277             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40278             this.list.setWidth(lw);
40279             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40280         }
40281         
40282     
40283         
40284     },
40285     
40286     addItem: function(rec)
40287     {
40288         var valueField = this.combo.valueField;
40289         var displayField = this.combo.displayField;
40290         if (this.items.indexOfKey(rec[valueField]) > -1) {
40291             //console.log("GOT " + rec.data.id);
40292             return;
40293         }
40294         
40295         var x = new Roo.form.ComboBoxArray.Item({
40296             //id : rec[this.idField],
40297             data : rec,
40298             displayField : displayField ,
40299             tipField : displayField ,
40300             cb : this
40301         });
40302         // use the 
40303         this.items.add(rec[valueField],x);
40304         // add it before the element..
40305         this.updateHiddenEl();
40306         x.render(this.outerWrap, this.wrap.dom);
40307         // add the image handler..
40308     },
40309     
40310     updateHiddenEl : function()
40311     {
40312         this.validate();
40313         if (!this.hiddenEl) {
40314             return;
40315         }
40316         var ar = [];
40317         var idField = this.combo.valueField;
40318         
40319         this.items.each(function(f) {
40320             ar.push(f.data[idField]);
40321            
40322         });
40323         this.hiddenEl.dom.value = ar.join(',');
40324         this.validate();
40325     },
40326     
40327     reset : function()
40328     {
40329         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40330         this.items.each(function(f) {
40331            f.remove(); 
40332         });
40333         this.el.dom.value = '';
40334         if (this.hiddenEl) {
40335             this.hiddenEl.dom.value = '';
40336         }
40337         
40338     },
40339     getValue: function()
40340     {
40341         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40342     },
40343     setValue: function(v) // not a valid action - must use addItems..
40344     {
40345          
40346         this.reset();
40347         
40348         
40349         
40350         if (this.store.isLocal && (typeof(v) == 'string')) {
40351             // then we can use the store to find the values..
40352             // comma seperated at present.. this needs to allow JSON based encoding..
40353             this.hiddenEl.value  = v;
40354             var v_ar = [];
40355             Roo.each(v.split(','), function(k) {
40356                 Roo.log("CHECK " + this.valueField + ',' + k);
40357                 var li = this.store.query(this.valueField, k);
40358                 if (!li.length) {
40359                     return;
40360                 }
40361                 var add = {};
40362                 add[this.valueField] = k;
40363                 add[this.displayField] = li.item(0).data[this.displayField];
40364                 
40365                 this.addItem(add);
40366             }, this) 
40367              
40368         }
40369         if (typeof(v) == 'object') {
40370             // then let's assume it's an array of objects..
40371             Roo.each(v, function(l) {
40372                 this.addItem(l);
40373             }, this);
40374              
40375         }
40376         
40377         
40378     },
40379     setFromData: function(v)
40380     {
40381         // this recieves an object, if setValues is called.
40382         this.reset();
40383         this.el.dom.value = v[this.displayField];
40384         this.hiddenEl.dom.value = v[this.valueField];
40385         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40386             return;
40387         }
40388         var kv = v[this.valueField];
40389         var dv = v[this.displayField];
40390         kv = typeof(kv) != 'string' ? '' : kv;
40391         dv = typeof(dv) != 'string' ? '' : dv;
40392         
40393         
40394         var keys = kv.split(',');
40395         var display = dv.split(',');
40396         for (var i = 0 ; i < keys.length; i++) {
40397             
40398             add = {};
40399             add[this.valueField] = keys[i];
40400             add[this.displayField] = display[i];
40401             this.addItem(add);
40402         }
40403       
40404         
40405     },
40406     
40407     /**
40408      * Validates the combox array value
40409      * @return {Boolean} True if the value is valid, else false
40410      */
40411     validate : function(){
40412         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40413             this.clearInvalid();
40414             return true;
40415         }
40416         return false;
40417     },
40418     
40419     validateValue : function(value){
40420         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40421         
40422     },
40423     
40424     /*@
40425      * overide
40426      * 
40427      */
40428     isDirty : function() {
40429         if(this.disabled) {
40430             return false;
40431         }
40432         
40433         try {
40434             var d = Roo.decode(String(this.originalValue));
40435         } catch (e) {
40436             return String(this.getValue()) !== String(this.originalValue);
40437         }
40438         
40439         var originalValue = [];
40440         
40441         for (var i = 0; i < d.length; i++){
40442             originalValue.push(d[i][this.valueField]);
40443         }
40444         
40445         return String(this.getValue()) !== String(originalValue.join(','));
40446         
40447     }
40448     
40449 });
40450
40451
40452
40453 /**
40454  * @class Roo.form.ComboBoxArray.Item
40455  * @extends Roo.BoxComponent
40456  * A selected item in the list
40457  *  Fred [x]  Brian [x]  [Pick another |v]
40458  * 
40459  * @constructor
40460  * Create a new item.
40461  * @param {Object} config Configuration options
40462  */
40463  
40464 Roo.form.ComboBoxArray.Item = function(config) {
40465     config.id = Roo.id();
40466     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40467 }
40468
40469 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40470     data : {},
40471     cb: false,
40472     displayField : false,
40473     tipField : false,
40474     
40475     
40476     defaultAutoCreate : {
40477         tag: 'div',
40478         cls: 'x-cbarray-item',
40479         cn : [ 
40480             { tag: 'div' },
40481             {
40482                 tag: 'img',
40483                 width:16,
40484                 height : 16,
40485                 src : Roo.BLANK_IMAGE_URL ,
40486                 align: 'center'
40487             }
40488         ]
40489         
40490     },
40491     
40492  
40493     onRender : function(ct, position)
40494     {
40495         Roo.form.Field.superclass.onRender.call(this, ct, position);
40496         
40497         if(!this.el){
40498             var cfg = this.getAutoCreate();
40499             this.el = ct.createChild(cfg, position);
40500         }
40501         
40502         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40503         
40504         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40505             this.cb.renderer(this.data) :
40506             String.format('{0}',this.data[this.displayField]);
40507         
40508             
40509         this.el.child('div').dom.setAttribute('qtip',
40510                         String.format('{0}',this.data[this.tipField])
40511         );
40512         
40513         this.el.child('img').on('click', this.remove, this);
40514         
40515     },
40516    
40517     remove : function()
40518     {
40519         
40520         this.cb.items.remove(this);
40521         this.el.child('img').un('click', this.remove, this);
40522         this.el.remove();
40523         this.cb.updateHiddenEl();
40524     }
40525 });/*
40526  * Based on:
40527  * Ext JS Library 1.1.1
40528  * Copyright(c) 2006-2007, Ext JS, LLC.
40529  *
40530  * Originally Released Under LGPL - original licence link has changed is not relivant.
40531  *
40532  * Fork - LGPL
40533  * <script type="text/javascript">
40534  */
40535 /**
40536  * @class Roo.form.Checkbox
40537  * @extends Roo.form.Field
40538  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40539  * @constructor
40540  * Creates a new Checkbox
40541  * @param {Object} config Configuration options
40542  */
40543 Roo.form.Checkbox = function(config){
40544     Roo.form.Checkbox.superclass.constructor.call(this, config);
40545     this.addEvents({
40546         /**
40547          * @event check
40548          * Fires when the checkbox is checked or unchecked.
40549              * @param {Roo.form.Checkbox} this This checkbox
40550              * @param {Boolean} checked The new checked value
40551              */
40552         check : true
40553     });
40554 };
40555
40556 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40557     /**
40558      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40559      */
40560     focusClass : undefined,
40561     /**
40562      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40563      */
40564     fieldClass: "x-form-field",
40565     /**
40566      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40567      */
40568     checked: false,
40569     /**
40570      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40571      * {tag: "input", type: "checkbox", autocomplete: "off"})
40572      */
40573     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40574     /**
40575      * @cfg {String} boxLabel The text that appears beside the checkbox
40576      */
40577     boxLabel : "",
40578     /**
40579      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40580      */  
40581     inputValue : '1',
40582     /**
40583      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40584      */
40585      valueOff: '0', // value when not checked..
40586
40587     actionMode : 'viewEl', 
40588     //
40589     // private
40590     itemCls : 'x-menu-check-item x-form-item',
40591     groupClass : 'x-menu-group-item',
40592     inputType : 'hidden',
40593     
40594     
40595     inSetChecked: false, // check that we are not calling self...
40596     
40597     inputElement: false, // real input element?
40598     basedOn: false, // ????
40599     
40600     isFormField: true, // not sure where this is needed!!!!
40601
40602     onResize : function(){
40603         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40604         if(!this.boxLabel){
40605             this.el.alignTo(this.wrap, 'c-c');
40606         }
40607     },
40608
40609     initEvents : function(){
40610         Roo.form.Checkbox.superclass.initEvents.call(this);
40611         this.el.on("click", this.onClick,  this);
40612         this.el.on("change", this.onClick,  this);
40613     },
40614
40615
40616     getResizeEl : function(){
40617         return this.wrap;
40618     },
40619
40620     getPositionEl : function(){
40621         return this.wrap;
40622     },
40623
40624     // private
40625     onRender : function(ct, position){
40626         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40627         /*
40628         if(this.inputValue !== undefined){
40629             this.el.dom.value = this.inputValue;
40630         }
40631         */
40632         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40633         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40634         var viewEl = this.wrap.createChild({ 
40635             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40636         this.viewEl = viewEl;   
40637         this.wrap.on('click', this.onClick,  this); 
40638         
40639         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40640         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40641         
40642         
40643         
40644         if(this.boxLabel){
40645             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40646         //    viewEl.on('click', this.onClick,  this); 
40647         }
40648         //if(this.checked){
40649             this.setChecked(this.checked);
40650         //}else{
40651             //this.checked = this.el.dom;
40652         //}
40653
40654     },
40655
40656     // private
40657     initValue : Roo.emptyFn,
40658
40659     /**
40660      * Returns the checked state of the checkbox.
40661      * @return {Boolean} True if checked, else false
40662      */
40663     getValue : function(){
40664         if(this.el){
40665             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40666         }
40667         return this.valueOff;
40668         
40669     },
40670
40671         // private
40672     onClick : function(){ 
40673         this.setChecked(!this.checked);
40674
40675         //if(this.el.dom.checked != this.checked){
40676         //    this.setValue(this.el.dom.checked);
40677        // }
40678     },
40679
40680     /**
40681      * Sets the checked state of the checkbox.
40682      * On is always based on a string comparison between inputValue and the param.
40683      * @param {Boolean/String} value - the value to set 
40684      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40685      */
40686     setValue : function(v,suppressEvent){
40687         
40688         
40689         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40690         //if(this.el && this.el.dom){
40691         //    this.el.dom.checked = this.checked;
40692         //    this.el.dom.defaultChecked = this.checked;
40693         //}
40694         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40695         //this.fireEvent("check", this, this.checked);
40696     },
40697     // private..
40698     setChecked : function(state,suppressEvent)
40699     {
40700         if (this.inSetChecked) {
40701             this.checked = state;
40702             return;
40703         }
40704         
40705     
40706         if(this.wrap){
40707             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40708         }
40709         this.checked = state;
40710         if(suppressEvent !== true){
40711             this.fireEvent('check', this, state);
40712         }
40713         this.inSetChecked = true;
40714         this.el.dom.value = state ? this.inputValue : this.valueOff;
40715         this.inSetChecked = false;
40716         
40717     },
40718     // handle setting of hidden value by some other method!!?!?
40719     setFromHidden: function()
40720     {
40721         if(!this.el){
40722             return;
40723         }
40724         //console.log("SET FROM HIDDEN");
40725         //alert('setFrom hidden');
40726         this.setValue(this.el.dom.value);
40727     },
40728     
40729     onDestroy : function()
40730     {
40731         if(this.viewEl){
40732             Roo.get(this.viewEl).remove();
40733         }
40734          
40735         Roo.form.Checkbox.superclass.onDestroy.call(this);
40736     }
40737
40738 });/*
40739  * Based on:
40740  * Ext JS Library 1.1.1
40741  * Copyright(c) 2006-2007, Ext JS, LLC.
40742  *
40743  * Originally Released Under LGPL - original licence link has changed is not relivant.
40744  *
40745  * Fork - LGPL
40746  * <script type="text/javascript">
40747  */
40748  
40749 /**
40750  * @class Roo.form.Radio
40751  * @extends Roo.form.Checkbox
40752  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40753  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40754  * @constructor
40755  * Creates a new Radio
40756  * @param {Object} config Configuration options
40757  */
40758 Roo.form.Radio = function(){
40759     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40760 };
40761 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40762     inputType: 'radio',
40763
40764     /**
40765      * If this radio is part of a group, it will return the selected value
40766      * @return {String}
40767      */
40768     getGroupValue : function(){
40769         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40770     },
40771     
40772     
40773     onRender : function(ct, position){
40774         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40775         
40776         if(this.inputValue !== undefined){
40777             this.el.dom.value = this.inputValue;
40778         }
40779          
40780         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40781         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40782         //var viewEl = this.wrap.createChild({ 
40783         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40784         //this.viewEl = viewEl;   
40785         //this.wrap.on('click', this.onClick,  this); 
40786         
40787         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40788         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40789         
40790         
40791         
40792         if(this.boxLabel){
40793             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40794         //    viewEl.on('click', this.onClick,  this); 
40795         }
40796          if(this.checked){
40797             this.el.dom.checked =   'checked' ;
40798         }
40799          
40800     } 
40801     
40802     
40803 });//<script type="text/javascript">
40804
40805 /*
40806  * Ext JS Library 1.1.1
40807  * Copyright(c) 2006-2007, Ext JS, LLC.
40808  * licensing@extjs.com
40809  * 
40810  * http://www.extjs.com/license
40811  */
40812  
40813  /*
40814   * 
40815   * Known bugs:
40816   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40817   * - IE ? - no idea how much works there.
40818   * 
40819   * 
40820   * 
40821   */
40822  
40823
40824 /**
40825  * @class Ext.form.HtmlEditor
40826  * @extends Ext.form.Field
40827  * Provides a lightweight HTML Editor component.
40828  *
40829  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40830  * 
40831  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40832  * supported by this editor.</b><br/><br/>
40833  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40834  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40835  */
40836 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40837       /**
40838      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40839      */
40840     toolbars : false,
40841     /**
40842      * @cfg {String} createLinkText The default text for the create link prompt
40843      */
40844     createLinkText : 'Please enter the URL for the link:',
40845     /**
40846      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40847      */
40848     defaultLinkValue : 'http:/'+'/',
40849    
40850      /**
40851      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40852      *                        Roo.resizable.
40853      */
40854     resizable : false,
40855      /**
40856      * @cfg {Number} height (in pixels)
40857      */   
40858     height: 300,
40859    /**
40860      * @cfg {Number} width (in pixels)
40861      */   
40862     width: 500,
40863     
40864     /**
40865      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40866      * 
40867      */
40868     stylesheets: false,
40869     
40870     // id of frame..
40871     frameId: false,
40872     
40873     // private properties
40874     validationEvent : false,
40875     deferHeight: true,
40876     initialized : false,
40877     activated : false,
40878     sourceEditMode : false,
40879     onFocus : Roo.emptyFn,
40880     iframePad:3,
40881     hideMode:'offsets',
40882     
40883     defaultAutoCreate : { // modified by initCompnoent..
40884         tag: "textarea",
40885         style:"width:500px;height:300px;",
40886         autocomplete: "off"
40887     },
40888
40889     // private
40890     initComponent : function(){
40891         this.addEvents({
40892             /**
40893              * @event initialize
40894              * Fires when the editor is fully initialized (including the iframe)
40895              * @param {HtmlEditor} this
40896              */
40897             initialize: true,
40898             /**
40899              * @event activate
40900              * Fires when the editor is first receives the focus. Any insertion must wait
40901              * until after this event.
40902              * @param {HtmlEditor} this
40903              */
40904             activate: true,
40905              /**
40906              * @event beforesync
40907              * Fires before the textarea is updated with content from the editor iframe. Return false
40908              * to cancel the sync.
40909              * @param {HtmlEditor} this
40910              * @param {String} html
40911              */
40912             beforesync: true,
40913              /**
40914              * @event beforepush
40915              * Fires before the iframe editor is updated with content from the textarea. Return false
40916              * to cancel the push.
40917              * @param {HtmlEditor} this
40918              * @param {String} html
40919              */
40920             beforepush: true,
40921              /**
40922              * @event sync
40923              * Fires when the textarea is updated with content from the editor iframe.
40924              * @param {HtmlEditor} this
40925              * @param {String} html
40926              */
40927             sync: true,
40928              /**
40929              * @event push
40930              * Fires when the iframe editor is updated with content from the textarea.
40931              * @param {HtmlEditor} this
40932              * @param {String} html
40933              */
40934             push: true,
40935              /**
40936              * @event editmodechange
40937              * Fires when the editor switches edit modes
40938              * @param {HtmlEditor} this
40939              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40940              */
40941             editmodechange: true,
40942             /**
40943              * @event editorevent
40944              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40945              * @param {HtmlEditor} this
40946              */
40947             editorevent: true
40948         });
40949         this.defaultAutoCreate =  {
40950             tag: "textarea",
40951             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40952             autocomplete: "off"
40953         };
40954     },
40955
40956     /**
40957      * Protected method that will not generally be called directly. It
40958      * is called when the editor creates its toolbar. Override this method if you need to
40959      * add custom toolbar buttons.
40960      * @param {HtmlEditor} editor
40961      */
40962     createToolbar : function(editor){
40963         if (!editor.toolbars || !editor.toolbars.length) {
40964             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40965         }
40966         
40967         for (var i =0 ; i < editor.toolbars.length;i++) {
40968             editor.toolbars[i] = Roo.factory(
40969                     typeof(editor.toolbars[i]) == 'string' ?
40970                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40971                 Roo.form.HtmlEditor);
40972             editor.toolbars[i].init(editor);
40973         }
40974          
40975         
40976     },
40977
40978     /**
40979      * Protected method that will not generally be called directly. It
40980      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40981      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40982      */
40983     getDocMarkup : function(){
40984         // body styles..
40985         var st = '';
40986         if (this.stylesheets === false) {
40987             
40988             Roo.get(document.head).select('style').each(function(node) {
40989                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40990             });
40991             
40992             Roo.get(document.head).select('link').each(function(node) { 
40993                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40994             });
40995             
40996         } else if (!this.stylesheets.length) {
40997                 // simple..
40998                 st = '<style type="text/css">' +
40999                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41000                    '</style>';
41001         } else {
41002             Roo.each(this.stylesheets, function(s) {
41003                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41004             });
41005             
41006         }
41007         
41008         st +=  '<style type="text/css">' +
41009             'IMG { cursor: pointer } ' +
41010         '</style>';
41011
41012         
41013         return '<html><head>' + st  +
41014             //<style type="text/css">' +
41015             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41016             //'</style>' +
41017             ' </head><body class="roo-htmleditor-body"></body></html>';
41018     },
41019
41020     // private
41021     onRender : function(ct, position)
41022     {
41023         var _t = this;
41024         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
41025         this.el.dom.style.border = '0 none';
41026         this.el.dom.setAttribute('tabIndex', -1);
41027         this.el.addClass('x-hidden');
41028         if(Roo.isIE){ // fix IE 1px bogus margin
41029             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41030         }
41031         this.wrap = this.el.wrap({
41032             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
41033         });
41034         
41035         if (this.resizable) {
41036             this.resizeEl = new Roo.Resizable(this.wrap, {
41037                 pinned : true,
41038                 wrap: true,
41039                 dynamic : true,
41040                 minHeight : this.height,
41041                 height: this.height,
41042                 handles : this.resizable,
41043                 width: this.width,
41044                 listeners : {
41045                     resize : function(r, w, h) {
41046                         _t.onResize(w,h); // -something
41047                     }
41048                 }
41049             });
41050             
41051         }
41052
41053         this.frameId = Roo.id();
41054         
41055         this.createToolbar(this);
41056         
41057       
41058         
41059         var iframe = this.wrap.createChild({
41060             tag: 'iframe',
41061             id: this.frameId,
41062             name: this.frameId,
41063             frameBorder : 'no',
41064             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41065         }, this.el
41066         );
41067         
41068        // console.log(iframe);
41069         //this.wrap.dom.appendChild(iframe);
41070
41071         this.iframe = iframe.dom;
41072
41073          this.assignDocWin();
41074         
41075         this.doc.designMode = 'on';
41076        
41077         this.doc.open();
41078         this.doc.write(this.getDocMarkup());
41079         this.doc.close();
41080
41081         
41082         var task = { // must defer to wait for browser to be ready
41083             run : function(){
41084                 //console.log("run task?" + this.doc.readyState);
41085                 this.assignDocWin();
41086                 if(this.doc.body || this.doc.readyState == 'complete'){
41087                     try {
41088                         this.doc.designMode="on";
41089                     } catch (e) {
41090                         return;
41091                     }
41092                     Roo.TaskMgr.stop(task);
41093                     this.initEditor.defer(10, this);
41094                 }
41095             },
41096             interval : 10,
41097             duration:10000,
41098             scope: this
41099         };
41100         Roo.TaskMgr.start(task);
41101
41102         if(!this.width){
41103             this.setSize(this.wrap.getSize());
41104         }
41105         if (this.resizeEl) {
41106             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41107             // should trigger onReize..
41108         }
41109     },
41110
41111     // private
41112     onResize : function(w, h)
41113     {
41114         //Roo.log('resize: ' +w + ',' + h );
41115         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41116         if(this.el && this.iframe){
41117             if(typeof w == 'number'){
41118                 var aw = w - this.wrap.getFrameWidth('lr');
41119                 this.el.setWidth(this.adjustWidth('textarea', aw));
41120                 this.iframe.style.width = aw + 'px';
41121             }
41122             if(typeof h == 'number'){
41123                 var tbh = 0;
41124                 for (var i =0; i < this.toolbars.length;i++) {
41125                     // fixme - ask toolbars for heights?
41126                     tbh += this.toolbars[i].tb.el.getHeight();
41127                     if (this.toolbars[i].footer) {
41128                         tbh += this.toolbars[i].footer.el.getHeight();
41129                     }
41130                 }
41131                 
41132                 
41133                 
41134                 
41135                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41136                 ah -= 5; // knock a few pixes off for look..
41137                 this.el.setHeight(this.adjustWidth('textarea', ah));
41138                 this.iframe.style.height = ah + 'px';
41139                 if(this.doc){
41140                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41141                 }
41142             }
41143         }
41144     },
41145
41146     /**
41147      * Toggles the editor between standard and source edit mode.
41148      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41149      */
41150     toggleSourceEdit : function(sourceEditMode){
41151         
41152         this.sourceEditMode = sourceEditMode === true;
41153         
41154         if(this.sourceEditMode){
41155 //            Roo.log('in');
41156 //            Roo.log(this.syncValue());
41157             this.syncValue();
41158             this.iframe.className = 'x-hidden';
41159             this.el.removeClass('x-hidden');
41160             this.el.dom.removeAttribute('tabIndex');
41161             this.el.focus();
41162         }else{
41163 //            Roo.log('out')
41164 //            Roo.log(this.pushValue()); 
41165             this.pushValue();
41166             this.iframe.className = '';
41167             this.el.addClass('x-hidden');
41168             this.el.dom.setAttribute('tabIndex', -1);
41169             this.deferFocus();
41170         }
41171         this.setSize(this.wrap.getSize());
41172         this.fireEvent('editmodechange', this, this.sourceEditMode);
41173     },
41174
41175     // private used internally
41176     createLink : function(){
41177         var url = prompt(this.createLinkText, this.defaultLinkValue);
41178         if(url && url != 'http:/'+'/'){
41179             this.relayCmd('createlink', url);
41180         }
41181     },
41182
41183     // private (for BoxComponent)
41184     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41185
41186     // private (for BoxComponent)
41187     getResizeEl : function(){
41188         return this.wrap;
41189     },
41190
41191     // private (for BoxComponent)
41192     getPositionEl : function(){
41193         return this.wrap;
41194     },
41195
41196     // private
41197     initEvents : function(){
41198         this.originalValue = this.getValue();
41199     },
41200
41201     /**
41202      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41203      * @method
41204      */
41205     markInvalid : Roo.emptyFn,
41206     /**
41207      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41208      * @method
41209      */
41210     clearInvalid : Roo.emptyFn,
41211
41212     setValue : function(v){
41213         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41214         this.pushValue();
41215     },
41216
41217     /**
41218      * Protected method that will not generally be called directly. If you need/want
41219      * custom HTML cleanup, this is the method you should override.
41220      * @param {String} html The HTML to be cleaned
41221      * return {String} The cleaned HTML
41222      */
41223     cleanHtml : function(html){
41224         html = String(html);
41225         if(html.length > 5){
41226             if(Roo.isSafari){ // strip safari nonsense
41227                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41228             }
41229         }
41230         if(html == '&nbsp;'){
41231             html = '';
41232         }
41233         return html;
41234     },
41235
41236     /**
41237      * Protected method that will not generally be called directly. Syncs the contents
41238      * of the editor iframe with the textarea.
41239      */
41240     syncValue : function(){
41241         if(this.initialized){
41242             var bd = (this.doc.body || this.doc.documentElement);
41243             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41244             var html = bd.innerHTML;
41245             if(Roo.isSafari){
41246                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41247                 var m = bs.match(/text-align:(.*?);/i);
41248                 if(m && m[1]){
41249                     html = '<div style="'+m[0]+'">' + html + '</div>';
41250                 }
41251             }
41252             html = this.cleanHtml(html);
41253             // fix up the special chars.. normaly like back quotes in word...
41254             // however we do not want to do this with chinese..
41255             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41256                 var cc = b.charCodeAt();
41257                 if (
41258                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41259                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41260                     (cc >= 0xf900 && cc < 0xfb00 )
41261                 ) {
41262                         return b;
41263                 }
41264                 return "&#"+cc+";" 
41265             });
41266             if(this.fireEvent('beforesync', this, html) !== false){
41267                 this.el.dom.value = html;
41268                 this.fireEvent('sync', this, html);
41269             }
41270         }
41271     },
41272
41273     /**
41274      * Protected method that will not generally be called directly. Pushes the value of the textarea
41275      * into the iframe editor.
41276      */
41277     pushValue : function(){
41278         if(this.initialized){
41279             var v = this.el.dom.value;
41280             
41281             if(v.length < 1){
41282                 v = '&#160;';
41283             }
41284             
41285             if(this.fireEvent('beforepush', this, v) !== false){
41286                 var d = (this.doc.body || this.doc.documentElement);
41287                 d.innerHTML = v;
41288                 this.cleanUpPaste();
41289                 this.el.dom.value = d.innerHTML;
41290                 this.fireEvent('push', this, v);
41291             }
41292         }
41293     },
41294
41295     // private
41296     deferFocus : function(){
41297         this.focus.defer(10, this);
41298     },
41299
41300     // doc'ed in Field
41301     focus : function(){
41302         if(this.win && !this.sourceEditMode){
41303             this.win.focus();
41304         }else{
41305             this.el.focus();
41306         }
41307     },
41308     
41309     assignDocWin: function()
41310     {
41311         var iframe = this.iframe;
41312         
41313          if(Roo.isIE){
41314             this.doc = iframe.contentWindow.document;
41315             this.win = iframe.contentWindow;
41316         } else {
41317             if (!Roo.get(this.frameId)) {
41318                 return;
41319             }
41320             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41321             this.win = Roo.get(this.frameId).dom.contentWindow;
41322         }
41323     },
41324     
41325     // private
41326     initEditor : function(){
41327         //console.log("INIT EDITOR");
41328         this.assignDocWin();
41329         
41330         
41331         
41332         this.doc.designMode="on";
41333         this.doc.open();
41334         this.doc.write(this.getDocMarkup());
41335         this.doc.close();
41336         
41337         var dbody = (this.doc.body || this.doc.documentElement);
41338         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41339         // this copies styles from the containing element into thsi one..
41340         // not sure why we need all of this..
41341         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41342         ss['background-attachment'] = 'fixed'; // w3c
41343         dbody.bgProperties = 'fixed'; // ie
41344         Roo.DomHelper.applyStyles(dbody, ss);
41345         Roo.EventManager.on(this.doc, {
41346             //'mousedown': this.onEditorEvent,
41347             'mouseup': this.onEditorEvent,
41348             'dblclick': this.onEditorEvent,
41349             'click': this.onEditorEvent,
41350             'keyup': this.onEditorEvent,
41351             buffer:100,
41352             scope: this
41353         });
41354         if(Roo.isGecko){
41355             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41356         }
41357         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41358             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41359         }
41360         this.initialized = true;
41361
41362         this.fireEvent('initialize', this);
41363         this.pushValue();
41364     },
41365
41366     // private
41367     onDestroy : function(){
41368         
41369         
41370         
41371         if(this.rendered){
41372             
41373             for (var i =0; i < this.toolbars.length;i++) {
41374                 // fixme - ask toolbars for heights?
41375                 this.toolbars[i].onDestroy();
41376             }
41377             
41378             this.wrap.dom.innerHTML = '';
41379             this.wrap.remove();
41380         }
41381     },
41382
41383     // private
41384     onFirstFocus : function(){
41385         
41386         this.assignDocWin();
41387         
41388         
41389         this.activated = true;
41390         for (var i =0; i < this.toolbars.length;i++) {
41391             this.toolbars[i].onFirstFocus();
41392         }
41393        
41394         if(Roo.isGecko){ // prevent silly gecko errors
41395             this.win.focus();
41396             var s = this.win.getSelection();
41397             if(!s.focusNode || s.focusNode.nodeType != 3){
41398                 var r = s.getRangeAt(0);
41399                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41400                 r.collapse(true);
41401                 this.deferFocus();
41402             }
41403             try{
41404                 this.execCmd('useCSS', true);
41405                 this.execCmd('styleWithCSS', false);
41406             }catch(e){}
41407         }
41408         this.fireEvent('activate', this);
41409     },
41410
41411     // private
41412     adjustFont: function(btn){
41413         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41414         //if(Roo.isSafari){ // safari
41415         //    adjust *= 2;
41416        // }
41417         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41418         if(Roo.isSafari){ // safari
41419             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41420             v =  (v < 10) ? 10 : v;
41421             v =  (v > 48) ? 48 : v;
41422             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41423             
41424         }
41425         
41426         
41427         v = Math.max(1, v+adjust);
41428         
41429         this.execCmd('FontSize', v  );
41430     },
41431
41432     onEditorEvent : function(e){
41433         this.fireEvent('editorevent', this, e);
41434       //  this.updateToolbar();
41435         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41436     },
41437
41438     insertTag : function(tg)
41439     {
41440         // could be a bit smarter... -> wrap the current selected tRoo..
41441         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41442             
41443             range = this.createRange(this.getSelection());
41444             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41445             wrappingNode.appendChild(range.extractContents());
41446             range.insertNode(wrappingNode);
41447
41448             return;
41449             
41450             
41451             
41452         }
41453         this.execCmd("formatblock",   tg);
41454         
41455     },
41456     
41457     insertText : function(txt)
41458     {
41459         
41460         
41461         var range = this.createRange();
41462         range.deleteContents();
41463                //alert(Sender.getAttribute('label'));
41464                
41465         range.insertNode(this.doc.createTextNode(txt));
41466     } ,
41467     
41468     // private
41469     relayBtnCmd : function(btn){
41470         this.relayCmd(btn.cmd);
41471     },
41472
41473     /**
41474      * Executes a Midas editor command on the editor document and performs necessary focus and
41475      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41476      * @param {String} cmd The Midas command
41477      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41478      */
41479     relayCmd : function(cmd, value){
41480         this.win.focus();
41481         this.execCmd(cmd, value);
41482         this.fireEvent('editorevent', this);
41483         //this.updateToolbar();
41484         this.deferFocus();
41485     },
41486
41487     /**
41488      * Executes a Midas editor command directly on the editor document.
41489      * For visual commands, you should use {@link #relayCmd} instead.
41490      * <b>This should only be called after the editor is initialized.</b>
41491      * @param {String} cmd The Midas command
41492      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41493      */
41494     execCmd : function(cmd, value){
41495         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41496         this.syncValue();
41497     },
41498  
41499  
41500    
41501     /**
41502      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41503      * to insert tRoo.
41504      * @param {String} text | dom node.. 
41505      */
41506     insertAtCursor : function(text)
41507     {
41508         
41509         
41510         
41511         if(!this.activated){
41512             return;
41513         }
41514         /*
41515         if(Roo.isIE){
41516             this.win.focus();
41517             var r = this.doc.selection.createRange();
41518             if(r){
41519                 r.collapse(true);
41520                 r.pasteHTML(text);
41521                 this.syncValue();
41522                 this.deferFocus();
41523             
41524             }
41525             return;
41526         }
41527         */
41528         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41529             this.win.focus();
41530             
41531             
41532             // from jquery ui (MIT licenced)
41533             var range, node;
41534             var win = this.win;
41535             
41536             if (win.getSelection && win.getSelection().getRangeAt) {
41537                 range = win.getSelection().getRangeAt(0);
41538                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41539                 range.insertNode(node);
41540             } else if (win.document.selection && win.document.selection.createRange) {
41541                 // no firefox support
41542                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41543                 win.document.selection.createRange().pasteHTML(txt);
41544             } else {
41545                 // no firefox support
41546                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41547                 this.execCmd('InsertHTML', txt);
41548             } 
41549             
41550             this.syncValue();
41551             
41552             this.deferFocus();
41553         }
41554     },
41555  // private
41556     mozKeyPress : function(e){
41557         if(e.ctrlKey){
41558             var c = e.getCharCode(), cmd;
41559           
41560             if(c > 0){
41561                 c = String.fromCharCode(c).toLowerCase();
41562                 switch(c){
41563                     case 'b':
41564                         cmd = 'bold';
41565                         break;
41566                     case 'i':
41567                         cmd = 'italic';
41568                         break;
41569                     
41570                     case 'u':
41571                         cmd = 'underline';
41572                         break;
41573                     
41574                     case 'v':
41575                         this.cleanUpPaste.defer(100, this);
41576                         return;
41577                         
41578                 }
41579                 if(cmd){
41580                     this.win.focus();
41581                     this.execCmd(cmd);
41582                     this.deferFocus();
41583                     e.preventDefault();
41584                 }
41585                 
41586             }
41587         }
41588     },
41589
41590     // private
41591     fixKeys : function(){ // load time branching for fastest keydown performance
41592         if(Roo.isIE){
41593             return function(e){
41594                 var k = e.getKey(), r;
41595                 if(k == e.TAB){
41596                     e.stopEvent();
41597                     r = this.doc.selection.createRange();
41598                     if(r){
41599                         r.collapse(true);
41600                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41601                         this.deferFocus();
41602                     }
41603                     return;
41604                 }
41605                 
41606                 if(k == e.ENTER){
41607                     r = this.doc.selection.createRange();
41608                     if(r){
41609                         var target = r.parentElement();
41610                         if(!target || target.tagName.toLowerCase() != 'li'){
41611                             e.stopEvent();
41612                             r.pasteHTML('<br />');
41613                             r.collapse(false);
41614                             r.select();
41615                         }
41616                     }
41617                 }
41618                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41619                     this.cleanUpPaste.defer(100, this);
41620                     return;
41621                 }
41622                 
41623                 
41624             };
41625         }else if(Roo.isOpera){
41626             return function(e){
41627                 var k = e.getKey();
41628                 if(k == e.TAB){
41629                     e.stopEvent();
41630                     this.win.focus();
41631                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41632                     this.deferFocus();
41633                 }
41634                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41635                     this.cleanUpPaste.defer(100, this);
41636                     return;
41637                 }
41638                 
41639             };
41640         }else if(Roo.isSafari){
41641             return function(e){
41642                 var k = e.getKey();
41643                 
41644                 if(k == e.TAB){
41645                     e.stopEvent();
41646                     this.execCmd('InsertText','\t');
41647                     this.deferFocus();
41648                     return;
41649                 }
41650                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41651                     this.cleanUpPaste.defer(100, this);
41652                     return;
41653                 }
41654                 
41655              };
41656         }
41657     }(),
41658     
41659     getAllAncestors: function()
41660     {
41661         var p = this.getSelectedNode();
41662         var a = [];
41663         if (!p) {
41664             a.push(p); // push blank onto stack..
41665             p = this.getParentElement();
41666         }
41667         
41668         
41669         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41670             a.push(p);
41671             p = p.parentNode;
41672         }
41673         a.push(this.doc.body);
41674         return a;
41675     },
41676     lastSel : false,
41677     lastSelNode : false,
41678     
41679     
41680     getSelection : function() 
41681     {
41682         this.assignDocWin();
41683         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41684     },
41685     
41686     getSelectedNode: function() 
41687     {
41688         // this may only work on Gecko!!!
41689         
41690         // should we cache this!!!!
41691         
41692         
41693         
41694          
41695         var range = this.createRange(this.getSelection()).cloneRange();
41696         
41697         if (Roo.isIE) {
41698             var parent = range.parentElement();
41699             while (true) {
41700                 var testRange = range.duplicate();
41701                 testRange.moveToElementText(parent);
41702                 if (testRange.inRange(range)) {
41703                     break;
41704                 }
41705                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41706                     break;
41707                 }
41708                 parent = parent.parentElement;
41709             }
41710             return parent;
41711         }
41712         
41713         // is ancestor a text element.
41714         var ac =  range.commonAncestorContainer;
41715         if (ac.nodeType == 3) {
41716             ac = ac.parentNode;
41717         }
41718         
41719         var ar = ac.childNodes;
41720          
41721         var nodes = [];
41722         var other_nodes = [];
41723         var has_other_nodes = false;
41724         for (var i=0;i<ar.length;i++) {
41725             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41726                 continue;
41727             }
41728             // fullly contained node.
41729             
41730             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41731                 nodes.push(ar[i]);
41732                 continue;
41733             }
41734             
41735             // probably selected..
41736             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41737                 other_nodes.push(ar[i]);
41738                 continue;
41739             }
41740             // outer..
41741             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41742                 continue;
41743             }
41744             
41745             
41746             has_other_nodes = true;
41747         }
41748         if (!nodes.length && other_nodes.length) {
41749             nodes= other_nodes;
41750         }
41751         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41752             return false;
41753         }
41754         
41755         return nodes[0];
41756     },
41757     createRange: function(sel)
41758     {
41759         // this has strange effects when using with 
41760         // top toolbar - not sure if it's a great idea.
41761         //this.editor.contentWindow.focus();
41762         if (typeof sel != "undefined") {
41763             try {
41764                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41765             } catch(e) {
41766                 return this.doc.createRange();
41767             }
41768         } else {
41769             return this.doc.createRange();
41770         }
41771     },
41772     getParentElement: function()
41773     {
41774         
41775         this.assignDocWin();
41776         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41777         
41778         var range = this.createRange(sel);
41779          
41780         try {
41781             var p = range.commonAncestorContainer;
41782             while (p.nodeType == 3) { // text node
41783                 p = p.parentNode;
41784             }
41785             return p;
41786         } catch (e) {
41787             return null;
41788         }
41789     
41790     },
41791     /***
41792      *
41793      * Range intersection.. the hard stuff...
41794      *  '-1' = before
41795      *  '0' = hits..
41796      *  '1' = after.
41797      *         [ -- selected range --- ]
41798      *   [fail]                        [fail]
41799      *
41800      *    basically..
41801      *      if end is before start or  hits it. fail.
41802      *      if start is after end or hits it fail.
41803      *
41804      *   if either hits (but other is outside. - then it's not 
41805      *   
41806      *    
41807      **/
41808     
41809     
41810     // @see http://www.thismuchiknow.co.uk/?p=64.
41811     rangeIntersectsNode : function(range, node)
41812     {
41813         var nodeRange = node.ownerDocument.createRange();
41814         try {
41815             nodeRange.selectNode(node);
41816         } catch (e) {
41817             nodeRange.selectNodeContents(node);
41818         }
41819     
41820         var rangeStartRange = range.cloneRange();
41821         rangeStartRange.collapse(true);
41822     
41823         var rangeEndRange = range.cloneRange();
41824         rangeEndRange.collapse(false);
41825     
41826         var nodeStartRange = nodeRange.cloneRange();
41827         nodeStartRange.collapse(true);
41828     
41829         var nodeEndRange = nodeRange.cloneRange();
41830         nodeEndRange.collapse(false);
41831     
41832         return rangeStartRange.compareBoundaryPoints(
41833                  Range.START_TO_START, nodeEndRange) == -1 &&
41834                rangeEndRange.compareBoundaryPoints(
41835                  Range.START_TO_START, nodeStartRange) == 1;
41836         
41837          
41838     },
41839     rangeCompareNode : function(range, node)
41840     {
41841         var nodeRange = node.ownerDocument.createRange();
41842         try {
41843             nodeRange.selectNode(node);
41844         } catch (e) {
41845             nodeRange.selectNodeContents(node);
41846         }
41847         
41848         
41849         range.collapse(true);
41850     
41851         nodeRange.collapse(true);
41852      
41853         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41854         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41855          
41856         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41857         
41858         var nodeIsBefore   =  ss == 1;
41859         var nodeIsAfter    = ee == -1;
41860         
41861         if (nodeIsBefore && nodeIsAfter)
41862             return 0; // outer
41863         if (!nodeIsBefore && nodeIsAfter)
41864             return 1; //right trailed.
41865         
41866         if (nodeIsBefore && !nodeIsAfter)
41867             return 2;  // left trailed.
41868         // fully contined.
41869         return 3;
41870     },
41871
41872     // private? - in a new class?
41873     cleanUpPaste :  function()
41874     {
41875         // cleans up the whole document..
41876          Roo.log('cleanuppaste');
41877         this.cleanUpChildren(this.doc.body);
41878         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41879         if (clean != this.doc.body.innerHTML) {
41880             this.doc.body.innerHTML = clean;
41881         }
41882         
41883     },
41884     
41885     cleanWordChars : function(input) {// change the chars to hex code
41886         var he = Roo.form.HtmlEditor;
41887         
41888         var output = input;
41889         Roo.each(he.swapCodes, function(sw) { 
41890             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41891             
41892             output = output.replace(swapper, sw[1]);
41893         });
41894         
41895         return output;
41896     },
41897     
41898     
41899     cleanUpChildren : function (n)
41900     {
41901         if (!n.childNodes.length) {
41902             return;
41903         }
41904         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41905            this.cleanUpChild(n.childNodes[i]);
41906         }
41907     },
41908     
41909     
41910         
41911     
41912     cleanUpChild : function (node)
41913     {
41914         var ed = this;
41915         //console.log(node);
41916         if (node.nodeName == "#text") {
41917             // clean up silly Windows -- stuff?
41918             return; 
41919         }
41920         if (node.nodeName == "#comment") {
41921             node.parentNode.removeChild(node);
41922             // clean up silly Windows -- stuff?
41923             return; 
41924         }
41925         
41926         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41927             // remove node.
41928             node.parentNode.removeChild(node);
41929             return;
41930             
41931         }
41932         
41933         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41934         
41935         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41936         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41937         
41938         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41939         //    remove_keep_children = true;
41940         //}
41941         
41942         if (remove_keep_children) {
41943             this.cleanUpChildren(node);
41944             // inserts everything just before this node...
41945             while (node.childNodes.length) {
41946                 var cn = node.childNodes[0];
41947                 node.removeChild(cn);
41948                 node.parentNode.insertBefore(cn, node);
41949             }
41950             node.parentNode.removeChild(node);
41951             return;
41952         }
41953         
41954         if (!node.attributes || !node.attributes.length) {
41955             this.cleanUpChildren(node);
41956             return;
41957         }
41958         
41959         function cleanAttr(n,v)
41960         {
41961             
41962             if (v.match(/^\./) || v.match(/^\//)) {
41963                 return;
41964             }
41965             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41966                 return;
41967             }
41968             if (v.match(/^#/)) {
41969                 return;
41970             }
41971 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41972             node.removeAttribute(n);
41973             
41974         }
41975         
41976         function cleanStyle(n,v)
41977         {
41978             if (v.match(/expression/)) { //XSS?? should we even bother..
41979                 node.removeAttribute(n);
41980                 return;
41981             }
41982             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41983             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41984             
41985             
41986             var parts = v.split(/;/);
41987             var clean = [];
41988             
41989             Roo.each(parts, function(p) {
41990                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41991                 if (!p.length) {
41992                     return true;
41993                 }
41994                 var l = p.split(':').shift().replace(/\s+/g,'');
41995                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41996                 
41997                 
41998                 if ( cblack.indexOf(l) > -1) {
41999 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42000                     //node.removeAttribute(n);
42001                     return true;
42002                 }
42003                 //Roo.log()
42004                 // only allow 'c whitelisted system attributes'
42005                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42006 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42007                     //node.removeAttribute(n);
42008                     return true;
42009                 }
42010                 
42011                 
42012                  
42013                 
42014                 clean.push(p);
42015                 return true;
42016             });
42017             if (clean.length) { 
42018                 node.setAttribute(n, clean.join(';'));
42019             } else {
42020                 node.removeAttribute(n);
42021             }
42022             
42023         }
42024         
42025         
42026         for (var i = node.attributes.length-1; i > -1 ; i--) {
42027             var a = node.attributes[i];
42028             //console.log(a);
42029             
42030             if (a.name.toLowerCase().substr(0,2)=='on')  {
42031                 node.removeAttribute(a.name);
42032                 continue;
42033             }
42034             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42035                 node.removeAttribute(a.name);
42036                 continue;
42037             }
42038             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42039                 cleanAttr(a.name,a.value); // fixme..
42040                 continue;
42041             }
42042             if (a.name == 'style') {
42043                 cleanStyle(a.name,a.value);
42044                 continue;
42045             }
42046             /// clean up MS crap..
42047             // tecnically this should be a list of valid class'es..
42048             
42049             
42050             if (a.name == 'class') {
42051                 if (a.value.match(/^Mso/)) {
42052                     node.className = '';
42053                 }
42054                 
42055                 if (a.value.match(/body/)) {
42056                     node.className = '';
42057                 }
42058                 continue;
42059             }
42060             
42061             // style cleanup!?
42062             // class cleanup?
42063             
42064         }
42065         
42066         
42067         this.cleanUpChildren(node);
42068         
42069         
42070     }
42071     
42072     
42073     // hide stuff that is not compatible
42074     /**
42075      * @event blur
42076      * @hide
42077      */
42078     /**
42079      * @event change
42080      * @hide
42081      */
42082     /**
42083      * @event focus
42084      * @hide
42085      */
42086     /**
42087      * @event specialkey
42088      * @hide
42089      */
42090     /**
42091      * @cfg {String} fieldClass @hide
42092      */
42093     /**
42094      * @cfg {String} focusClass @hide
42095      */
42096     /**
42097      * @cfg {String} autoCreate @hide
42098      */
42099     /**
42100      * @cfg {String} inputType @hide
42101      */
42102     /**
42103      * @cfg {String} invalidClass @hide
42104      */
42105     /**
42106      * @cfg {String} invalidText @hide
42107      */
42108     /**
42109      * @cfg {String} msgFx @hide
42110      */
42111     /**
42112      * @cfg {String} validateOnBlur @hide
42113      */
42114 });
42115
42116 Roo.form.HtmlEditor.white = [
42117         'area', 'br', 'img', 'input', 'hr', 'wbr',
42118         
42119        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42120        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42121        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42122        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42123        'table',   'ul',         'xmp', 
42124        
42125        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42126       'thead',   'tr', 
42127      
42128       'dir', 'menu', 'ol', 'ul', 'dl',
42129        
42130       'embed',  'object'
42131 ];
42132
42133
42134 Roo.form.HtmlEditor.black = [
42135     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42136         'applet', // 
42137         'base',   'basefont', 'bgsound', 'blink',  'body', 
42138         'frame',  'frameset', 'head',    'html',   'ilayer', 
42139         'iframe', 'layer',  'link',     'meta',    'object',   
42140         'script', 'style' ,'title',  'xml' // clean later..
42141 ];
42142 Roo.form.HtmlEditor.clean = [
42143     'script', 'style', 'title', 'xml'
42144 ];
42145 Roo.form.HtmlEditor.remove = [
42146     'font'
42147 ];
42148 // attributes..
42149
42150 Roo.form.HtmlEditor.ablack = [
42151     'on'
42152 ];
42153     
42154 Roo.form.HtmlEditor.aclean = [ 
42155     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42156 ];
42157
42158 // protocols..
42159 Roo.form.HtmlEditor.pwhite= [
42160         'http',  'https',  'mailto'
42161 ];
42162
42163 // white listed style attributes.
42164 Roo.form.HtmlEditor.cwhite= [
42165       //  'text-align', /// default is to allow most things..
42166       
42167          
42168 //        'font-size'//??
42169 ];
42170
42171 // black listed style attributes.
42172 Roo.form.HtmlEditor.cblack= [
42173       //  'font-size' -- this can be set by the project 
42174 ];
42175
42176
42177 Roo.form.HtmlEditor.swapCodes   =[ 
42178     [    8211, "--" ], 
42179     [    8212, "--" ], 
42180     [    8216,  "'" ],  
42181     [    8217, "'" ],  
42182     [    8220, '"' ],  
42183     [    8221, '"' ],  
42184     [    8226, "*" ],  
42185     [    8230, "..." ]
42186 ]; 
42187
42188     // <script type="text/javascript">
42189 /*
42190  * Based on
42191  * Ext JS Library 1.1.1
42192  * Copyright(c) 2006-2007, Ext JS, LLC.
42193  *  
42194  
42195  */
42196
42197 /**
42198  * @class Roo.form.HtmlEditorToolbar1
42199  * Basic Toolbar
42200  * 
42201  * Usage:
42202  *
42203  new Roo.form.HtmlEditor({
42204     ....
42205     toolbars : [
42206         new Roo.form.HtmlEditorToolbar1({
42207             disable : { fonts: 1 , format: 1, ..., ... , ...],
42208             btns : [ .... ]
42209         })
42210     }
42211      
42212  * 
42213  * @cfg {Object} disable List of elements to disable..
42214  * @cfg {Array} btns List of additional buttons.
42215  * 
42216  * 
42217  * NEEDS Extra CSS? 
42218  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42219  */
42220  
42221 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42222 {
42223     
42224     Roo.apply(this, config);
42225     
42226     // default disabled, based on 'good practice'..
42227     this.disable = this.disable || {};
42228     Roo.applyIf(this.disable, {
42229         fontSize : true,
42230         colors : true,
42231         specialElements : true
42232     });
42233     
42234     
42235     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42236     // dont call parent... till later.
42237 }
42238
42239 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42240     
42241     tb: false,
42242     
42243     rendered: false,
42244     
42245     editor : false,
42246     /**
42247      * @cfg {Object} disable  List of toolbar elements to disable
42248          
42249      */
42250     disable : false,
42251       /**
42252      * @cfg {Array} fontFamilies An array of available font families
42253      */
42254     fontFamilies : [
42255         'Arial',
42256         'Courier New',
42257         'Tahoma',
42258         'Times New Roman',
42259         'Verdana'
42260     ],
42261     
42262     specialChars : [
42263            "&#169;",
42264           "&#174;",     
42265           "&#8482;",    
42266           "&#163;" ,    
42267          // "&#8212;",    
42268           "&#8230;",    
42269           "&#247;" ,    
42270         //  "&#225;" ,     ?? a acute?
42271            "&#8364;"    , //Euro
42272        //   "&#8220;"    ,
42273         //  "&#8221;"    ,
42274         //  "&#8226;"    ,
42275           "&#176;"  //   , // degrees
42276
42277          // "&#233;"     , // e ecute
42278          // "&#250;"     , // u ecute?
42279     ],
42280     
42281     specialElements : [
42282         {
42283             text: "Insert Table",
42284             xtype: 'MenuItem',
42285             xns : Roo.Menu,
42286             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42287                 
42288         },
42289         {    
42290             text: "Insert Image",
42291             xtype: 'MenuItem',
42292             xns : Roo.Menu,
42293             ihtml : '<img src="about:blank"/>'
42294             
42295         }
42296         
42297          
42298     ],
42299     
42300     
42301     inputElements : [ 
42302             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42303             "input:submit", "input:button", "select", "textarea", "label" ],
42304     formats : [
42305         ["p"] ,  
42306         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42307         ["pre"],[ "code"], 
42308         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42309         ['div'],['span']
42310     ],
42311     
42312     cleanStyles : [
42313         "font-size"
42314     ],
42315      /**
42316      * @cfg {String} defaultFont default font to use.
42317      */
42318     defaultFont: 'tahoma',
42319    
42320     fontSelect : false,
42321     
42322     
42323     formatCombo : false,
42324     
42325     init : function(editor)
42326     {
42327         this.editor = editor;
42328         
42329         
42330         var fid = editor.frameId;
42331         var etb = this;
42332         function btn(id, toggle, handler){
42333             var xid = fid + '-'+ id ;
42334             return {
42335                 id : xid,
42336                 cmd : id,
42337                 cls : 'x-btn-icon x-edit-'+id,
42338                 enableToggle:toggle !== false,
42339                 scope: editor, // was editor...
42340                 handler:handler||editor.relayBtnCmd,
42341                 clickEvent:'mousedown',
42342                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42343                 tabIndex:-1
42344             };
42345         }
42346         
42347         
42348         
42349         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42350         this.tb = tb;
42351          // stop form submits
42352         tb.el.on('click', function(e){
42353             e.preventDefault(); // what does this do?
42354         });
42355
42356         if(!this.disable.font) { // && !Roo.isSafari){
42357             /* why no safari for fonts 
42358             editor.fontSelect = tb.el.createChild({
42359                 tag:'select',
42360                 tabIndex: -1,
42361                 cls:'x-font-select',
42362                 html: this.createFontOptions()
42363             });
42364             
42365             editor.fontSelect.on('change', function(){
42366                 var font = editor.fontSelect.dom.value;
42367                 editor.relayCmd('fontname', font);
42368                 editor.deferFocus();
42369             }, editor);
42370             
42371             tb.add(
42372                 editor.fontSelect.dom,
42373                 '-'
42374             );
42375             */
42376             
42377         };
42378         if(!this.disable.formats){
42379             this.formatCombo = new Roo.form.ComboBox({
42380                 store: new Roo.data.SimpleStore({
42381                     id : 'tag',
42382                     fields: ['tag'],
42383                     data : this.formats // from states.js
42384                 }),
42385                 blockFocus : true,
42386                 name : '',
42387                 //autoCreate : {tag: "div",  size: "20"},
42388                 displayField:'tag',
42389                 typeAhead: false,
42390                 mode: 'local',
42391                 editable : false,
42392                 triggerAction: 'all',
42393                 emptyText:'Add tag',
42394                 selectOnFocus:true,
42395                 width:135,
42396                 listeners : {
42397                     'select': function(c, r, i) {
42398                         editor.insertTag(r.get('tag'));
42399                         editor.focus();
42400                     }
42401                 }
42402
42403             });
42404             tb.addField(this.formatCombo);
42405             
42406         }
42407         
42408         if(!this.disable.format){
42409             tb.add(
42410                 btn('bold'),
42411                 btn('italic'),
42412                 btn('underline')
42413             );
42414         };
42415         if(!this.disable.fontSize){
42416             tb.add(
42417                 '-',
42418                 
42419                 
42420                 btn('increasefontsize', false, editor.adjustFont),
42421                 btn('decreasefontsize', false, editor.adjustFont)
42422             );
42423         };
42424         
42425         
42426         if(!this.disable.colors){
42427             tb.add(
42428                 '-', {
42429                     id:editor.frameId +'-forecolor',
42430                     cls:'x-btn-icon x-edit-forecolor',
42431                     clickEvent:'mousedown',
42432                     tooltip: this.buttonTips['forecolor'] || undefined,
42433                     tabIndex:-1,
42434                     menu : new Roo.menu.ColorMenu({
42435                         allowReselect: true,
42436                         focus: Roo.emptyFn,
42437                         value:'000000',
42438                         plain:true,
42439                         selectHandler: function(cp, color){
42440                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42441                             editor.deferFocus();
42442                         },
42443                         scope: editor,
42444                         clickEvent:'mousedown'
42445                     })
42446                 }, {
42447                     id:editor.frameId +'backcolor',
42448                     cls:'x-btn-icon x-edit-backcolor',
42449                     clickEvent:'mousedown',
42450                     tooltip: this.buttonTips['backcolor'] || undefined,
42451                     tabIndex:-1,
42452                     menu : new Roo.menu.ColorMenu({
42453                         focus: Roo.emptyFn,
42454                         value:'FFFFFF',
42455                         plain:true,
42456                         allowReselect: true,
42457                         selectHandler: function(cp, color){
42458                             if(Roo.isGecko){
42459                                 editor.execCmd('useCSS', false);
42460                                 editor.execCmd('hilitecolor', color);
42461                                 editor.execCmd('useCSS', true);
42462                                 editor.deferFocus();
42463                             }else{
42464                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42465                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42466                                 editor.deferFocus();
42467                             }
42468                         },
42469                         scope:editor,
42470                         clickEvent:'mousedown'
42471                     })
42472                 }
42473             );
42474         };
42475         // now add all the items...
42476         
42477
42478         if(!this.disable.alignments){
42479             tb.add(
42480                 '-',
42481                 btn('justifyleft'),
42482                 btn('justifycenter'),
42483                 btn('justifyright')
42484             );
42485         };
42486
42487         //if(!Roo.isSafari){
42488             if(!this.disable.links){
42489                 tb.add(
42490                     '-',
42491                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42492                 );
42493             };
42494
42495             if(!this.disable.lists){
42496                 tb.add(
42497                     '-',
42498                     btn('insertorderedlist'),
42499                     btn('insertunorderedlist')
42500                 );
42501             }
42502             if(!this.disable.sourceEdit){
42503                 tb.add(
42504                     '-',
42505                     btn('sourceedit', true, function(btn){
42506                         this.toggleSourceEdit(btn.pressed);
42507                     })
42508                 );
42509             }
42510         //}
42511         
42512         var smenu = { };
42513         // special menu.. - needs to be tidied up..
42514         if (!this.disable.special) {
42515             smenu = {
42516                 text: "&#169;",
42517                 cls: 'x-edit-none',
42518                 
42519                 menu : {
42520                     items : []
42521                 }
42522             };
42523             for (var i =0; i < this.specialChars.length; i++) {
42524                 smenu.menu.items.push({
42525                     
42526                     html: this.specialChars[i],
42527                     handler: function(a,b) {
42528                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42529                         //editor.insertAtCursor(a.html);
42530                         
42531                     },
42532                     tabIndex:-1
42533                 });
42534             }
42535             
42536             
42537             tb.add(smenu);
42538             
42539             
42540         }
42541         
42542         var cmenu = { };
42543         if (!this.disable.cleanStyles) {
42544             cmenu = {
42545                 cls: 'x-btn-icon x-btn-clear',
42546                 
42547                 menu : {
42548                     items : []
42549                 }
42550             };
42551             for (var i =0; i < this.cleanStyles.length; i++) {
42552                 cmenu.menu.items.push({
42553                     actiontype : this.cleanStyles[i],
42554                     html: 'Remove ' + this.cleanStyles[i],
42555                     handler: function(a,b) {
42556                         Roo.log(a);
42557                         Roo.log(b);
42558                         var c = Roo.get(editor.doc.body);
42559                         c.select('[style]').each(function(s) {
42560                             s.dom.style.removeProperty(a.actiontype);
42561                         });
42562                         
42563                     },
42564                     tabIndex:-1
42565                 });
42566             }
42567             
42568             tb.add(cmenu);
42569         }
42570          
42571         if (!this.disable.specialElements) {
42572             var semenu = {
42573                 text: "Other;",
42574                 cls: 'x-edit-none',
42575                 menu : {
42576                     items : []
42577                 }
42578             };
42579             for (var i =0; i < this.specialElements.length; i++) {
42580                 semenu.menu.items.push(
42581                     Roo.apply({ 
42582                         handler: function(a,b) {
42583                             editor.insertAtCursor(this.ihtml);
42584                         }
42585                     }, this.specialElements[i])
42586                 );
42587                     
42588             }
42589             
42590             tb.add(semenu);
42591             
42592             
42593         }
42594          
42595         
42596         if (this.btns) {
42597             for(var i =0; i< this.btns.length;i++) {
42598                 var b = Roo.factory(this.btns[i],Roo.form);
42599                 b.cls =  'x-edit-none';
42600                 b.scope = editor;
42601                 tb.add(b);
42602             }
42603         
42604         }
42605         
42606         
42607         
42608         // disable everything...
42609         
42610         this.tb.items.each(function(item){
42611            if(item.id != editor.frameId+ '-sourceedit'){
42612                 item.disable();
42613             }
42614         });
42615         this.rendered = true;
42616         
42617         // the all the btns;
42618         editor.on('editorevent', this.updateToolbar, this);
42619         // other toolbars need to implement this..
42620         //editor.on('editmodechange', this.updateToolbar, this);
42621     },
42622     
42623     
42624     
42625     /**
42626      * Protected method that will not generally be called directly. It triggers
42627      * a toolbar update by reading the markup state of the current selection in the editor.
42628      */
42629     updateToolbar: function(){
42630
42631         if(!this.editor.activated){
42632             this.editor.onFirstFocus();
42633             return;
42634         }
42635
42636         var btns = this.tb.items.map, 
42637             doc = this.editor.doc,
42638             frameId = this.editor.frameId;
42639
42640         if(!this.disable.font && !Roo.isSafari){
42641             /*
42642             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42643             if(name != this.fontSelect.dom.value){
42644                 this.fontSelect.dom.value = name;
42645             }
42646             */
42647         }
42648         if(!this.disable.format){
42649             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42650             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42651             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42652         }
42653         if(!this.disable.alignments){
42654             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42655             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42656             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42657         }
42658         if(!Roo.isSafari && !this.disable.lists){
42659             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42660             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42661         }
42662         
42663         var ans = this.editor.getAllAncestors();
42664         if (this.formatCombo) {
42665             
42666             
42667             var store = this.formatCombo.store;
42668             this.formatCombo.setValue("");
42669             for (var i =0; i < ans.length;i++) {
42670                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42671                     // select it..
42672                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42673                     break;
42674                 }
42675             }
42676         }
42677         
42678         
42679         
42680         // hides menus... - so this cant be on a menu...
42681         Roo.menu.MenuMgr.hideAll();
42682
42683         //this.editorsyncValue();
42684     },
42685    
42686     
42687     createFontOptions : function(){
42688         var buf = [], fs = this.fontFamilies, ff, lc;
42689         
42690         
42691         
42692         for(var i = 0, len = fs.length; i< len; i++){
42693             ff = fs[i];
42694             lc = ff.toLowerCase();
42695             buf.push(
42696                 '<option value="',lc,'" style="font-family:',ff,';"',
42697                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42698                     ff,
42699                 '</option>'
42700             );
42701         }
42702         return buf.join('');
42703     },
42704     
42705     toggleSourceEdit : function(sourceEditMode){
42706         if(sourceEditMode === undefined){
42707             sourceEditMode = !this.sourceEditMode;
42708         }
42709         this.sourceEditMode = sourceEditMode === true;
42710         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42711         // just toggle the button?
42712         if(btn.pressed !== this.editor.sourceEditMode){
42713             btn.toggle(this.editor.sourceEditMode);
42714             return;
42715         }
42716         
42717         if(this.sourceEditMode){
42718             this.tb.items.each(function(item){
42719                 if(item.cmd != 'sourceedit'){
42720                     item.disable();
42721                 }
42722             });
42723           
42724         }else{
42725             if(this.initialized){
42726                 this.tb.items.each(function(item){
42727                     item.enable();
42728                 });
42729             }
42730             
42731         }
42732         // tell the editor that it's been pressed..
42733         this.editor.toggleSourceEdit(sourceEditMode);
42734        
42735     },
42736      /**
42737      * Object collection of toolbar tooltips for the buttons in the editor. The key
42738      * is the command id associated with that button and the value is a valid QuickTips object.
42739      * For example:
42740 <pre><code>
42741 {
42742     bold : {
42743         title: 'Bold (Ctrl+B)',
42744         text: 'Make the selected text bold.',
42745         cls: 'x-html-editor-tip'
42746     },
42747     italic : {
42748         title: 'Italic (Ctrl+I)',
42749         text: 'Make the selected text italic.',
42750         cls: 'x-html-editor-tip'
42751     },
42752     ...
42753 </code></pre>
42754     * @type Object
42755      */
42756     buttonTips : {
42757         bold : {
42758             title: 'Bold (Ctrl+B)',
42759             text: 'Make the selected text bold.',
42760             cls: 'x-html-editor-tip'
42761         },
42762         italic : {
42763             title: 'Italic (Ctrl+I)',
42764             text: 'Make the selected text italic.',
42765             cls: 'x-html-editor-tip'
42766         },
42767         underline : {
42768             title: 'Underline (Ctrl+U)',
42769             text: 'Underline the selected text.',
42770             cls: 'x-html-editor-tip'
42771         },
42772         increasefontsize : {
42773             title: 'Grow Text',
42774             text: 'Increase the font size.',
42775             cls: 'x-html-editor-tip'
42776         },
42777         decreasefontsize : {
42778             title: 'Shrink Text',
42779             text: 'Decrease the font size.',
42780             cls: 'x-html-editor-tip'
42781         },
42782         backcolor : {
42783             title: 'Text Highlight Color',
42784             text: 'Change the background color of the selected text.',
42785             cls: 'x-html-editor-tip'
42786         },
42787         forecolor : {
42788             title: 'Font Color',
42789             text: 'Change the color of the selected text.',
42790             cls: 'x-html-editor-tip'
42791         },
42792         justifyleft : {
42793             title: 'Align Text Left',
42794             text: 'Align text to the left.',
42795             cls: 'x-html-editor-tip'
42796         },
42797         justifycenter : {
42798             title: 'Center Text',
42799             text: 'Center text in the editor.',
42800             cls: 'x-html-editor-tip'
42801         },
42802         justifyright : {
42803             title: 'Align Text Right',
42804             text: 'Align text to the right.',
42805             cls: 'x-html-editor-tip'
42806         },
42807         insertunorderedlist : {
42808             title: 'Bullet List',
42809             text: 'Start a bulleted list.',
42810             cls: 'x-html-editor-tip'
42811         },
42812         insertorderedlist : {
42813             title: 'Numbered List',
42814             text: 'Start a numbered list.',
42815             cls: 'x-html-editor-tip'
42816         },
42817         createlink : {
42818             title: 'Hyperlink',
42819             text: 'Make the selected text a hyperlink.',
42820             cls: 'x-html-editor-tip'
42821         },
42822         sourceedit : {
42823             title: 'Source Edit',
42824             text: 'Switch to source editing mode.',
42825             cls: 'x-html-editor-tip'
42826         }
42827     },
42828     // private
42829     onDestroy : function(){
42830         if(this.rendered){
42831             
42832             this.tb.items.each(function(item){
42833                 if(item.menu){
42834                     item.menu.removeAll();
42835                     if(item.menu.el){
42836                         item.menu.el.destroy();
42837                     }
42838                 }
42839                 item.destroy();
42840             });
42841              
42842         }
42843     },
42844     onFirstFocus: function() {
42845         this.tb.items.each(function(item){
42846            item.enable();
42847         });
42848     }
42849 });
42850
42851
42852
42853
42854 // <script type="text/javascript">
42855 /*
42856  * Based on
42857  * Ext JS Library 1.1.1
42858  * Copyright(c) 2006-2007, Ext JS, LLC.
42859  *  
42860  
42861  */
42862
42863  
42864 /**
42865  * @class Roo.form.HtmlEditor.ToolbarContext
42866  * Context Toolbar
42867  * 
42868  * Usage:
42869  *
42870  new Roo.form.HtmlEditor({
42871     ....
42872     toolbars : [
42873         { xtype: 'ToolbarStandard', styles : {} }
42874         { xtype: 'ToolbarContext', disable : {} }
42875     ]
42876 })
42877
42878      
42879  * 
42880  * @config : {Object} disable List of elements to disable.. (not done yet.)
42881  * @config : {Object} styles  Map of styles available.
42882  * 
42883  */
42884
42885 Roo.form.HtmlEditor.ToolbarContext = function(config)
42886 {
42887     
42888     Roo.apply(this, config);
42889     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42890     // dont call parent... till later.
42891     this.styles = this.styles || {};
42892 }
42893
42894  
42895
42896 Roo.form.HtmlEditor.ToolbarContext.types = {
42897     'IMG' : {
42898         width : {
42899             title: "Width",
42900             width: 40
42901         },
42902         height:  {
42903             title: "Height",
42904             width: 40
42905         },
42906         align: {
42907             title: "Align",
42908             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42909             width : 80
42910             
42911         },
42912         border: {
42913             title: "Border",
42914             width: 40
42915         },
42916         alt: {
42917             title: "Alt",
42918             width: 120
42919         },
42920         src : {
42921             title: "Src",
42922             width: 220
42923         }
42924         
42925     },
42926     'A' : {
42927         name : {
42928             title: "Name",
42929             width: 50
42930         },
42931         target:  {
42932             title: "Target",
42933             width: 120
42934         },
42935         href:  {
42936             title: "Href",
42937             width: 220
42938         } // border?
42939         
42940     },
42941     'TABLE' : {
42942         rows : {
42943             title: "Rows",
42944             width: 20
42945         },
42946         cols : {
42947             title: "Cols",
42948             width: 20
42949         },
42950         width : {
42951             title: "Width",
42952             width: 40
42953         },
42954         height : {
42955             title: "Height",
42956             width: 40
42957         },
42958         border : {
42959             title: "Border",
42960             width: 20
42961         }
42962     },
42963     'TD' : {
42964         width : {
42965             title: "Width",
42966             width: 40
42967         },
42968         height : {
42969             title: "Height",
42970             width: 40
42971         },   
42972         align: {
42973             title: "Align",
42974             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42975             width: 80
42976         },
42977         valign: {
42978             title: "Valign",
42979             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42980             width: 80
42981         },
42982         colspan: {
42983             title: "Colspan",
42984             width: 20
42985             
42986         },
42987          'font-family'  : {
42988             title : "Font",
42989             style : 'fontFamily',
42990             displayField: 'display',
42991             optname : 'font-family',
42992             width: 140
42993         }
42994     },
42995     'INPUT' : {
42996         name : {
42997             title: "name",
42998             width: 120
42999         },
43000         value : {
43001             title: "Value",
43002             width: 120
43003         },
43004         width : {
43005             title: "Width",
43006             width: 40
43007         }
43008     },
43009     'LABEL' : {
43010         'for' : {
43011             title: "For",
43012             width: 120
43013         }
43014     },
43015     'TEXTAREA' : {
43016           name : {
43017             title: "name",
43018             width: 120
43019         },
43020         rows : {
43021             title: "Rows",
43022             width: 20
43023         },
43024         cols : {
43025             title: "Cols",
43026             width: 20
43027         }
43028     },
43029     'SELECT' : {
43030         name : {
43031             title: "name",
43032             width: 120
43033         },
43034         selectoptions : {
43035             title: "Options",
43036             width: 200
43037         }
43038     },
43039     
43040     // should we really allow this??
43041     // should this just be 
43042     'BODY' : {
43043         title : {
43044             title: "Title",
43045             width: 200,
43046             disabled : true
43047         }
43048     },
43049     'SPAN' : {
43050         'font-family'  : {
43051             title : "Font",
43052             style : 'fontFamily',
43053             displayField: 'display',
43054             optname : 'font-family',
43055             width: 140
43056         }
43057     },
43058     'DIV' : {
43059         'font-family'  : {
43060             title : "Font",
43061             style : 'fontFamily',
43062             displayField: 'display',
43063             optname : 'font-family',
43064             width: 140
43065         }
43066     },
43067      'P' : {
43068         'font-family'  : {
43069             title : "Font",
43070             style : 'fontFamily',
43071             displayField: 'display',
43072             optname : 'font-family',
43073             width: 140
43074         }
43075     },
43076     
43077     '*' : {
43078         // empty..
43079     }
43080
43081 };
43082
43083 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43084 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43085
43086 Roo.form.HtmlEditor.ToolbarContext.options = {
43087         'font-family'  : [ 
43088                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43089                 [ 'Courier New', 'Courier New'],
43090                 [ 'Tahoma', 'Tahoma'],
43091                 [ 'Times New Roman,serif', 'Times'],
43092                 [ 'Verdana','Verdana' ]
43093         ]
43094 };
43095
43096 // fixme - these need to be configurable..
43097  
43098
43099 Roo.form.HtmlEditor.ToolbarContext.types
43100
43101
43102 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43103     
43104     tb: false,
43105     
43106     rendered: false,
43107     
43108     editor : false,
43109     /**
43110      * @cfg {Object} disable  List of toolbar elements to disable
43111          
43112      */
43113     disable : false,
43114     /**
43115      * @cfg {Object} styles List of styles 
43116      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43117      *
43118      * These must be defined in the page, so they get rendered correctly..
43119      * .headline { }
43120      * TD.underline { }
43121      * 
43122      */
43123     styles : false,
43124     
43125     options: false,
43126     
43127     toolbars : false,
43128     
43129     init : function(editor)
43130     {
43131         this.editor = editor;
43132         
43133         
43134         var fid = editor.frameId;
43135         var etb = this;
43136         function btn(id, toggle, handler){
43137             var xid = fid + '-'+ id ;
43138             return {
43139                 id : xid,
43140                 cmd : id,
43141                 cls : 'x-btn-icon x-edit-'+id,
43142                 enableToggle:toggle !== false,
43143                 scope: editor, // was editor...
43144                 handler:handler||editor.relayBtnCmd,
43145                 clickEvent:'mousedown',
43146                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43147                 tabIndex:-1
43148             };
43149         }
43150         // create a new element.
43151         var wdiv = editor.wrap.createChild({
43152                 tag: 'div'
43153             }, editor.wrap.dom.firstChild.nextSibling, true);
43154         
43155         // can we do this more than once??
43156         
43157          // stop form submits
43158       
43159  
43160         // disable everything...
43161         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43162         this.toolbars = {};
43163            
43164         for (var i in  ty) {
43165           
43166             this.toolbars[i] = this.buildToolbar(ty[i],i);
43167         }
43168         this.tb = this.toolbars.BODY;
43169         this.tb.el.show();
43170         this.buildFooter();
43171         this.footer.show();
43172         editor.on('hide', function( ) { this.footer.hide() }, this);
43173         editor.on('show', function( ) { this.footer.show() }, this);
43174         
43175          
43176         this.rendered = true;
43177         
43178         // the all the btns;
43179         editor.on('editorevent', this.updateToolbar, this);
43180         // other toolbars need to implement this..
43181         //editor.on('editmodechange', this.updateToolbar, this);
43182     },
43183     
43184     
43185     
43186     /**
43187      * Protected method that will not generally be called directly. It triggers
43188      * a toolbar update by reading the markup state of the current selection in the editor.
43189      */
43190     updateToolbar: function(editor,ev,sel){
43191
43192         //Roo.log(ev);
43193         // capture mouse up - this is handy for selecting images..
43194         // perhaps should go somewhere else...
43195         if(!this.editor.activated){
43196              this.editor.onFirstFocus();
43197             return;
43198         }
43199         
43200         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43201         // selectNode - might want to handle IE?
43202         if (ev &&
43203             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43204             ev.target && ev.target.tagName == 'IMG') {
43205             // they have click on an image...
43206             // let's see if we can change the selection...
43207             sel = ev.target;
43208          
43209               var nodeRange = sel.ownerDocument.createRange();
43210             try {
43211                 nodeRange.selectNode(sel);
43212             } catch (e) {
43213                 nodeRange.selectNodeContents(sel);
43214             }
43215             //nodeRange.collapse(true);
43216             var s = editor.win.getSelection();
43217             s.removeAllRanges();
43218             s.addRange(nodeRange);
43219         }  
43220         
43221       
43222         var updateFooter = sel ? false : true;
43223         
43224         
43225         var ans = this.editor.getAllAncestors();
43226         
43227         // pick
43228         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43229         
43230         if (!sel) { 
43231             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43232             sel = sel ? sel : this.editor.doc.body;
43233             sel = sel.tagName.length ? sel : this.editor.doc.body;
43234             
43235         }
43236         // pick a menu that exists..
43237         var tn = sel.tagName.toUpperCase();
43238         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43239         
43240         tn = sel.tagName.toUpperCase();
43241         
43242         var lastSel = this.tb.selectedNode
43243         
43244         this.tb.selectedNode = sel;
43245         
43246         // if current menu does not match..
43247         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43248                 
43249             this.tb.el.hide();
43250             ///console.log("show: " + tn);
43251             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43252             this.tb.el.show();
43253             // update name
43254             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43255             
43256             
43257             // update attributes
43258             if (this.tb.fields) {
43259                 this.tb.fields.each(function(e) {
43260                     if (e.stylename) {
43261                         e.setValue(sel.style[e.stylename]);
43262                         return;
43263                     } 
43264                    e.setValue(sel.getAttribute(e.attrname));
43265                 });
43266             }
43267             
43268             var hasStyles = false;
43269             for(var i in this.styles) {
43270                 hasStyles = true;
43271                 break;
43272             }
43273             
43274             // update styles
43275             if (hasStyles) { 
43276                 var st = this.tb.fields.item(0);
43277                 
43278                 st.store.removeAll();
43279                
43280                 
43281                 var cn = sel.className.split(/\s+/);
43282                 
43283                 var avs = [];
43284                 if (this.styles['*']) {
43285                     
43286                     Roo.each(this.styles['*'], function(v) {
43287                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43288                     });
43289                 }
43290                 if (this.styles[tn]) { 
43291                     Roo.each(this.styles[tn], function(v) {
43292                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43293                     });
43294                 }
43295                 
43296                 st.store.loadData(avs);
43297                 st.collapse();
43298                 st.setValue(cn);
43299             }
43300             // flag our selected Node.
43301             this.tb.selectedNode = sel;
43302            
43303            
43304             Roo.menu.MenuMgr.hideAll();
43305
43306         }
43307         
43308         if (!updateFooter) {
43309             //this.footDisp.dom.innerHTML = ''; 
43310             return;
43311         }
43312         // update the footer
43313         //
43314         var html = '';
43315         
43316         this.footerEls = ans.reverse();
43317         Roo.each(this.footerEls, function(a,i) {
43318             if (!a) { return; }
43319             html += html.length ? ' &gt; '  :  '';
43320             
43321             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43322             
43323         });
43324        
43325         // 
43326         var sz = this.footDisp.up('td').getSize();
43327         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43328         this.footDisp.dom.style.marginLeft = '5px';
43329         
43330         this.footDisp.dom.style.overflow = 'hidden';
43331         
43332         this.footDisp.dom.innerHTML = html;
43333             
43334         //this.editorsyncValue();
43335     },
43336      
43337     
43338    
43339        
43340     // private
43341     onDestroy : function(){
43342         if(this.rendered){
43343             
43344             this.tb.items.each(function(item){
43345                 if(item.menu){
43346                     item.menu.removeAll();
43347                     if(item.menu.el){
43348                         item.menu.el.destroy();
43349                     }
43350                 }
43351                 item.destroy();
43352             });
43353              
43354         }
43355     },
43356     onFirstFocus: function() {
43357         // need to do this for all the toolbars..
43358         this.tb.items.each(function(item){
43359            item.enable();
43360         });
43361     },
43362     buildToolbar: function(tlist, nm)
43363     {
43364         var editor = this.editor;
43365          // create a new element.
43366         var wdiv = editor.wrap.createChild({
43367                 tag: 'div'
43368             }, editor.wrap.dom.firstChild.nextSibling, true);
43369         
43370        
43371         var tb = new Roo.Toolbar(wdiv);
43372         // add the name..
43373         
43374         tb.add(nm+ ":&nbsp;");
43375         
43376         var styles = [];
43377         for(var i in this.styles) {
43378             styles.push(i);
43379         }
43380         
43381         // styles...
43382         if (styles && styles.length) {
43383             
43384             // this needs a multi-select checkbox...
43385             tb.addField( new Roo.form.ComboBox({
43386                 store: new Roo.data.SimpleStore({
43387                     id : 'val',
43388                     fields: ['val', 'selected'],
43389                     data : [] 
43390                 }),
43391                 name : '-roo-edit-className',
43392                 attrname : 'className',
43393                 displayField: 'val',
43394                 typeAhead: false,
43395                 mode: 'local',
43396                 editable : false,
43397                 triggerAction: 'all',
43398                 emptyText:'Select Style',
43399                 selectOnFocus:true,
43400                 width: 130,
43401                 listeners : {
43402                     'select': function(c, r, i) {
43403                         // initial support only for on class per el..
43404                         tb.selectedNode.className =  r ? r.get('val') : '';
43405                         editor.syncValue();
43406                     }
43407                 }
43408     
43409             }));
43410         }
43411         
43412         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43413         var tbops = tbc.options;
43414         
43415         for (var i in tlist) {
43416             
43417             var item = tlist[i];
43418             tb.add(item.title + ":&nbsp;");
43419             
43420             
43421             //optname == used so you can configure the options available..
43422             var opts = item.opts ? item.opts : false;
43423             if (item.optname) {
43424                 opts = tbops[item.optname];
43425            
43426             }
43427             
43428             if (opts) {
43429                 // opts == pulldown..
43430                 tb.addField( new Roo.form.ComboBox({
43431                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43432                         id : 'val',
43433                         fields: ['val', 'display'],
43434                         data : opts  
43435                     }),
43436                     name : '-roo-edit-' + i,
43437                     attrname : i,
43438                     stylename : item.style ? item.style : false,
43439                     displayField: item.displayField ? item.displayField : 'val',
43440                     valueField :  'val',
43441                     typeAhead: false,
43442                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43443                     editable : false,
43444                     triggerAction: 'all',
43445                     emptyText:'Select',
43446                     selectOnFocus:true,
43447                     width: item.width ? item.width  : 130,
43448                     listeners : {
43449                         'select': function(c, r, i) {
43450                             if (c.stylename) {
43451                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43452                                 return;
43453                             }
43454                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43455                         }
43456                     }
43457
43458                 }));
43459                 continue;
43460                     
43461                  
43462                 
43463                 tb.addField( new Roo.form.TextField({
43464                     name: i,
43465                     width: 100,
43466                     //allowBlank:false,
43467                     value: ''
43468                 }));
43469                 continue;
43470             }
43471             tb.addField( new Roo.form.TextField({
43472                 name: '-roo-edit-' + i,
43473                 attrname : i,
43474                 
43475                 width: item.width,
43476                 //allowBlank:true,
43477                 value: '',
43478                 listeners: {
43479                     'change' : function(f, nv, ov) {
43480                         tb.selectedNode.setAttribute(f.attrname, nv);
43481                     }
43482                 }
43483             }));
43484              
43485         }
43486         tb.addFill();
43487         var _this = this;
43488         tb.addButton( {
43489             text: 'Remove Tag',
43490     
43491             listeners : {
43492                 click : function ()
43493                 {
43494                     // remove
43495                     // undo does not work.
43496                      
43497                     var sn = tb.selectedNode;
43498                     
43499                     var pn = sn.parentNode;
43500                     
43501                     var stn =  sn.childNodes[0];
43502                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43503                     while (sn.childNodes.length) {
43504                         var node = sn.childNodes[0];
43505                         sn.removeChild(node);
43506                         //Roo.log(node);
43507                         pn.insertBefore(node, sn);
43508                         
43509                     }
43510                     pn.removeChild(sn);
43511                     var range = editor.createRange();
43512         
43513                     range.setStart(stn,0);
43514                     range.setEnd(en,0); //????
43515                     //range.selectNode(sel);
43516                     
43517                     
43518                     var selection = editor.getSelection();
43519                     selection.removeAllRanges();
43520                     selection.addRange(range);
43521                     
43522                     
43523                     
43524                     //_this.updateToolbar(null, null, pn);
43525                     _this.updateToolbar(null, null, null);
43526                     _this.footDisp.dom.innerHTML = ''; 
43527                 }
43528             }
43529             
43530                     
43531                 
43532             
43533         });
43534         
43535         
43536         tb.el.on('click', function(e){
43537             e.preventDefault(); // what does this do?
43538         });
43539         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43540         tb.el.hide();
43541         tb.name = nm;
43542         // dont need to disable them... as they will get hidden
43543         return tb;
43544          
43545         
43546     },
43547     buildFooter : function()
43548     {
43549         
43550         var fel = this.editor.wrap.createChild();
43551         this.footer = new Roo.Toolbar(fel);
43552         // toolbar has scrolly on left / right?
43553         var footDisp= new Roo.Toolbar.Fill();
43554         var _t = this;
43555         this.footer.add(
43556             {
43557                 text : '&lt;',
43558                 xtype: 'Button',
43559                 handler : function() {
43560                     _t.footDisp.scrollTo('left',0,true)
43561                 }
43562             }
43563         );
43564         this.footer.add( footDisp );
43565         this.footer.add( 
43566             {
43567                 text : '&gt;',
43568                 xtype: 'Button',
43569                 handler : function() {
43570                     // no animation..
43571                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43572                 }
43573             }
43574         );
43575         var fel = Roo.get(footDisp.el);
43576         fel.addClass('x-editor-context');
43577         this.footDispWrap = fel; 
43578         this.footDispWrap.overflow  = 'hidden';
43579         
43580         this.footDisp = fel.createChild();
43581         this.footDispWrap.on('click', this.onContextClick, this)
43582         
43583         
43584     },
43585     onContextClick : function (ev,dom)
43586     {
43587         ev.preventDefault();
43588         var  cn = dom.className;
43589         //Roo.log(cn);
43590         if (!cn.match(/x-ed-loc-/)) {
43591             return;
43592         }
43593         var n = cn.split('-').pop();
43594         var ans = this.footerEls;
43595         var sel = ans[n];
43596         
43597          // pick
43598         var range = this.editor.createRange();
43599         
43600         range.selectNodeContents(sel);
43601         //range.selectNode(sel);
43602         
43603         
43604         var selection = this.editor.getSelection();
43605         selection.removeAllRanges();
43606         selection.addRange(range);
43607         
43608         
43609         
43610         this.updateToolbar(null, null, sel);
43611         
43612         
43613     }
43614     
43615     
43616     
43617     
43618     
43619 });
43620
43621
43622
43623
43624
43625 /*
43626  * Based on:
43627  * Ext JS Library 1.1.1
43628  * Copyright(c) 2006-2007, Ext JS, LLC.
43629  *
43630  * Originally Released Under LGPL - original licence link has changed is not relivant.
43631  *
43632  * Fork - LGPL
43633  * <script type="text/javascript">
43634  */
43635  
43636 /**
43637  * @class Roo.form.BasicForm
43638  * @extends Roo.util.Observable
43639  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43640  * @constructor
43641  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43642  * @param {Object} config Configuration options
43643  */
43644 Roo.form.BasicForm = function(el, config){
43645     this.allItems = [];
43646     this.childForms = [];
43647     Roo.apply(this, config);
43648     /*
43649      * The Roo.form.Field items in this form.
43650      * @type MixedCollection
43651      */
43652      
43653      
43654     this.items = new Roo.util.MixedCollection(false, function(o){
43655         return o.id || (o.id = Roo.id());
43656     });
43657     this.addEvents({
43658         /**
43659          * @event beforeaction
43660          * Fires before any action is performed. Return false to cancel the action.
43661          * @param {Form} this
43662          * @param {Action} action The action to be performed
43663          */
43664         beforeaction: true,
43665         /**
43666          * @event actionfailed
43667          * Fires when an action fails.
43668          * @param {Form} this
43669          * @param {Action} action The action that failed
43670          */
43671         actionfailed : true,
43672         /**
43673          * @event actioncomplete
43674          * Fires when an action is completed.
43675          * @param {Form} this
43676          * @param {Action} action The action that completed
43677          */
43678         actioncomplete : true
43679     });
43680     if(el){
43681         this.initEl(el);
43682     }
43683     Roo.form.BasicForm.superclass.constructor.call(this);
43684 };
43685
43686 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43687     /**
43688      * @cfg {String} method
43689      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43690      */
43691     /**
43692      * @cfg {DataReader} reader
43693      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43694      * This is optional as there is built-in support for processing JSON.
43695      */
43696     /**
43697      * @cfg {DataReader} errorReader
43698      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43699      * This is completely optional as there is built-in support for processing JSON.
43700      */
43701     /**
43702      * @cfg {String} url
43703      * The URL to use for form actions if one isn't supplied in the action options.
43704      */
43705     /**
43706      * @cfg {Boolean} fileUpload
43707      * Set to true if this form is a file upload.
43708      */
43709      
43710     /**
43711      * @cfg {Object} baseParams
43712      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43713      */
43714      /**
43715      
43716     /**
43717      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43718      */
43719     timeout: 30,
43720
43721     // private
43722     activeAction : null,
43723
43724     /**
43725      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43726      * or setValues() data instead of when the form was first created.
43727      */
43728     trackResetOnLoad : false,
43729     
43730     
43731     /**
43732      * childForms - used for multi-tab forms
43733      * @type {Array}
43734      */
43735     childForms : false,
43736     
43737     /**
43738      * allItems - full list of fields.
43739      * @type {Array}
43740      */
43741     allItems : false,
43742     
43743     /**
43744      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43745      * element by passing it or its id or mask the form itself by passing in true.
43746      * @type Mixed
43747      */
43748     waitMsgTarget : false,
43749
43750     // private
43751     initEl : function(el){
43752         this.el = Roo.get(el);
43753         this.id = this.el.id || Roo.id();
43754         this.el.on('submit', this.onSubmit, this);
43755         this.el.addClass('x-form');
43756     },
43757
43758     // private
43759     onSubmit : function(e){
43760         e.stopEvent();
43761     },
43762
43763     /**
43764      * Returns true if client-side validation on the form is successful.
43765      * @return Boolean
43766      */
43767     isValid : function(){
43768         var valid = true;
43769         this.items.each(function(f){
43770            if(!f.validate()){
43771                valid = false;
43772            }
43773         });
43774         return valid;
43775     },
43776
43777     /**
43778      * Returns true if any fields in this form have changed since their original load.
43779      * @return Boolean
43780      */
43781     isDirty : function(){
43782         var dirty = false;
43783         this.items.each(function(f){
43784            if(f.isDirty()){
43785                dirty = true;
43786                return false;
43787            }
43788         });
43789         return dirty;
43790     },
43791
43792     /**
43793      * Performs a predefined action (submit or load) or custom actions you define on this form.
43794      * @param {String} actionName The name of the action type
43795      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43796      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43797      * accept other config options):
43798      * <pre>
43799 Property          Type             Description
43800 ----------------  ---------------  ----------------------------------------------------------------------------------
43801 url               String           The url for the action (defaults to the form's url)
43802 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43803 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43804 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43805                                    validate the form on the client (defaults to false)
43806      * </pre>
43807      * @return {BasicForm} this
43808      */
43809     doAction : function(action, options){
43810         if(typeof action == 'string'){
43811             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43812         }
43813         if(this.fireEvent('beforeaction', this, action) !== false){
43814             this.beforeAction(action);
43815             action.run.defer(100, action);
43816         }
43817         return this;
43818     },
43819
43820     /**
43821      * Shortcut to do a submit action.
43822      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43823      * @return {BasicForm} this
43824      */
43825     submit : function(options){
43826         this.doAction('submit', options);
43827         return this;
43828     },
43829
43830     /**
43831      * Shortcut to do a load action.
43832      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43833      * @return {BasicForm} this
43834      */
43835     load : function(options){
43836         this.doAction('load', options);
43837         return this;
43838     },
43839
43840     /**
43841      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43842      * @param {Record} record The record to edit
43843      * @return {BasicForm} this
43844      */
43845     updateRecord : function(record){
43846         record.beginEdit();
43847         var fs = record.fields;
43848         fs.each(function(f){
43849             var field = this.findField(f.name);
43850             if(field){
43851                 record.set(f.name, field.getValue());
43852             }
43853         }, this);
43854         record.endEdit();
43855         return this;
43856     },
43857
43858     /**
43859      * Loads an Roo.data.Record into this form.
43860      * @param {Record} record The record to load
43861      * @return {BasicForm} this
43862      */
43863     loadRecord : function(record){
43864         this.setValues(record.data);
43865         return this;
43866     },
43867
43868     // private
43869     beforeAction : function(action){
43870         var o = action.options;
43871         
43872        
43873         if(this.waitMsgTarget === true){
43874             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43875         }else if(this.waitMsgTarget){
43876             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43877             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43878         }else {
43879             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43880         }
43881          
43882     },
43883
43884     // private
43885     afterAction : function(action, success){
43886         this.activeAction = null;
43887         var o = action.options;
43888         
43889         if(this.waitMsgTarget === true){
43890             this.el.unmask();
43891         }else if(this.waitMsgTarget){
43892             this.waitMsgTarget.unmask();
43893         }else{
43894             Roo.MessageBox.updateProgress(1);
43895             Roo.MessageBox.hide();
43896         }
43897          
43898         if(success){
43899             if(o.reset){
43900                 this.reset();
43901             }
43902             Roo.callback(o.success, o.scope, [this, action]);
43903             this.fireEvent('actioncomplete', this, action);
43904             
43905         }else{
43906             
43907             // failure condition..
43908             // we have a scenario where updates need confirming.
43909             // eg. if a locking scenario exists..
43910             // we look for { errors : { needs_confirm : true }} in the response.
43911             if (
43912                 (typeof(action.result) != 'undefined')  &&
43913                 (typeof(action.result.errors) != 'undefined')  &&
43914                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43915            ){
43916                 var _t = this;
43917                 Roo.MessageBox.confirm(
43918                     "Change requires confirmation",
43919                     action.result.errorMsg,
43920                     function(r) {
43921                         if (r != 'yes') {
43922                             return;
43923                         }
43924                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43925                     }
43926                     
43927                 );
43928                 
43929                 
43930                 
43931                 return;
43932             }
43933             
43934             Roo.callback(o.failure, o.scope, [this, action]);
43935             // show an error message if no failed handler is set..
43936             if (!this.hasListener('actionfailed')) {
43937                 Roo.MessageBox.alert("Error",
43938                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43939                         action.result.errorMsg :
43940                         "Saving Failed, please check your entries or try again"
43941                 );
43942             }
43943             
43944             this.fireEvent('actionfailed', this, action);
43945         }
43946         
43947     },
43948
43949     /**
43950      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43951      * @param {String} id The value to search for
43952      * @return Field
43953      */
43954     findField : function(id){
43955         var field = this.items.get(id);
43956         if(!field){
43957             this.items.each(function(f){
43958                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43959                     field = f;
43960                     return false;
43961                 }
43962             });
43963         }
43964         return field || null;
43965     },
43966
43967     /**
43968      * Add a secondary form to this one, 
43969      * Used to provide tabbed forms. One form is primary, with hidden values 
43970      * which mirror the elements from the other forms.
43971      * 
43972      * @param {Roo.form.Form} form to add.
43973      * 
43974      */
43975     addForm : function(form)
43976     {
43977        
43978         if (this.childForms.indexOf(form) > -1) {
43979             // already added..
43980             return;
43981         }
43982         this.childForms.push(form);
43983         var n = '';
43984         Roo.each(form.allItems, function (fe) {
43985             
43986             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43987             if (this.findField(n)) { // already added..
43988                 return;
43989             }
43990             var add = new Roo.form.Hidden({
43991                 name : n
43992             });
43993             add.render(this.el);
43994             
43995             this.add( add );
43996         }, this);
43997         
43998     },
43999     /**
44000      * Mark fields in this form invalid in bulk.
44001      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44002      * @return {BasicForm} this
44003      */
44004     markInvalid : function(errors){
44005         if(errors instanceof Array){
44006             for(var i = 0, len = errors.length; i < len; i++){
44007                 var fieldError = errors[i];
44008                 var f = this.findField(fieldError.id);
44009                 if(f){
44010                     f.markInvalid(fieldError.msg);
44011                 }
44012             }
44013         }else{
44014             var field, id;
44015             for(id in errors){
44016                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44017                     field.markInvalid(errors[id]);
44018                 }
44019             }
44020         }
44021         Roo.each(this.childForms || [], function (f) {
44022             f.markInvalid(errors);
44023         });
44024         
44025         return this;
44026     },
44027
44028     /**
44029      * Set values for fields in this form in bulk.
44030      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44031      * @return {BasicForm} this
44032      */
44033     setValues : function(values){
44034         if(values instanceof Array){ // array of objects
44035             for(var i = 0, len = values.length; i < len; i++){
44036                 var v = values[i];
44037                 var f = this.findField(v.id);
44038                 if(f){
44039                     f.setValue(v.value);
44040                     if(this.trackResetOnLoad){
44041                         f.originalValue = f.getValue();
44042                     }
44043                 }
44044             }
44045         }else{ // object hash
44046             var field, id;
44047             for(id in values){
44048                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44049                     
44050                     if (field.setFromData && 
44051                         field.valueField && 
44052                         field.displayField &&
44053                         // combos' with local stores can 
44054                         // be queried via setValue()
44055                         // to set their value..
44056                         (field.store && !field.store.isLocal)
44057                         ) {
44058                         // it's a combo
44059                         var sd = { };
44060                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44061                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44062                         field.setFromData(sd);
44063                         
44064                     } else {
44065                         field.setValue(values[id]);
44066                     }
44067                     
44068                     
44069                     if(this.trackResetOnLoad){
44070                         field.originalValue = field.getValue();
44071                     }
44072                 }
44073             }
44074         }
44075          
44076         Roo.each(this.childForms || [], function (f) {
44077             f.setValues(values);
44078         });
44079                 
44080         return this;
44081     },
44082
44083     /**
44084      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44085      * they are returned as an array.
44086      * @param {Boolean} asString
44087      * @return {Object}
44088      */
44089     getValues : function(asString){
44090         if (this.childForms) {
44091             // copy values from the child forms
44092             Roo.each(this.childForms, function (f) {
44093                 this.setValues(f.getValues());
44094             }, this);
44095         }
44096         
44097         
44098         
44099         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44100         if(asString === true){
44101             return fs;
44102         }
44103         return Roo.urlDecode(fs);
44104     },
44105     
44106     /**
44107      * Returns the fields in this form as an object with key/value pairs. 
44108      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44109      * @return {Object}
44110      */
44111     getFieldValues : function(with_hidden)
44112     {
44113         if (this.childForms) {
44114             // copy values from the child forms
44115             // should this call getFieldValues - probably not as we do not currently copy
44116             // hidden fields when we generate..
44117             Roo.each(this.childForms, function (f) {
44118                 this.setValues(f.getValues());
44119             }, this);
44120         }
44121         
44122         var ret = {};
44123         this.items.each(function(f){
44124             if (!f.getName()) {
44125                 return;
44126             }
44127             var v = f.getValue();
44128             if (f.inputType =='radio') {
44129                 if (typeof(ret[f.getName()]) == 'undefined') {
44130                     ret[f.getName()] = ''; // empty..
44131                 }
44132                 
44133                 if (!f.el.dom.checked) {
44134                     return;
44135                     
44136                 }
44137                 v = f.el.dom.value;
44138                 
44139             }
44140             
44141             // not sure if this supported any more..
44142             if ((typeof(v) == 'object') && f.getRawValue) {
44143                 v = f.getRawValue() ; // dates..
44144             }
44145             // combo boxes where name != hiddenName...
44146             if (f.name != f.getName()) {
44147                 ret[f.name] = f.getRawValue();
44148             }
44149             ret[f.getName()] = v;
44150         });
44151         
44152         return ret;
44153     },
44154
44155     /**
44156      * Clears all invalid messages in this form.
44157      * @return {BasicForm} this
44158      */
44159     clearInvalid : function(){
44160         this.items.each(function(f){
44161            f.clearInvalid();
44162         });
44163         
44164         Roo.each(this.childForms || [], function (f) {
44165             f.clearInvalid();
44166         });
44167         
44168         
44169         return this;
44170     },
44171
44172     /**
44173      * Resets this form.
44174      * @return {BasicForm} this
44175      */
44176     reset : function(){
44177         this.items.each(function(f){
44178             f.reset();
44179         });
44180         
44181         Roo.each(this.childForms || [], function (f) {
44182             f.reset();
44183         });
44184        
44185         
44186         return this;
44187     },
44188
44189     /**
44190      * Add Roo.form components to this form.
44191      * @param {Field} field1
44192      * @param {Field} field2 (optional)
44193      * @param {Field} etc (optional)
44194      * @return {BasicForm} this
44195      */
44196     add : function(){
44197         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44198         return this;
44199     },
44200
44201
44202     /**
44203      * Removes a field from the items collection (does NOT remove its markup).
44204      * @param {Field} field
44205      * @return {BasicForm} this
44206      */
44207     remove : function(field){
44208         this.items.remove(field);
44209         return this;
44210     },
44211
44212     /**
44213      * Looks at the fields in this form, checks them for an id attribute,
44214      * and calls applyTo on the existing dom element with that id.
44215      * @return {BasicForm} this
44216      */
44217     render : function(){
44218         this.items.each(function(f){
44219             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44220                 f.applyTo(f.id);
44221             }
44222         });
44223         return this;
44224     },
44225
44226     /**
44227      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44228      * @param {Object} values
44229      * @return {BasicForm} this
44230      */
44231     applyToFields : function(o){
44232         this.items.each(function(f){
44233            Roo.apply(f, o);
44234         });
44235         return this;
44236     },
44237
44238     /**
44239      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44240      * @param {Object} values
44241      * @return {BasicForm} this
44242      */
44243     applyIfToFields : function(o){
44244         this.items.each(function(f){
44245            Roo.applyIf(f, o);
44246         });
44247         return this;
44248     }
44249 });
44250
44251 // back compat
44252 Roo.BasicForm = Roo.form.BasicForm;/*
44253  * Based on:
44254  * Ext JS Library 1.1.1
44255  * Copyright(c) 2006-2007, Ext JS, LLC.
44256  *
44257  * Originally Released Under LGPL - original licence link has changed is not relivant.
44258  *
44259  * Fork - LGPL
44260  * <script type="text/javascript">
44261  */
44262
44263 /**
44264  * @class Roo.form.Form
44265  * @extends Roo.form.BasicForm
44266  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44267  * @constructor
44268  * @param {Object} config Configuration options
44269  */
44270 Roo.form.Form = function(config){
44271     var xitems =  [];
44272     if (config.items) {
44273         xitems = config.items;
44274         delete config.items;
44275     }
44276    
44277     
44278     Roo.form.Form.superclass.constructor.call(this, null, config);
44279     this.url = this.url || this.action;
44280     if(!this.root){
44281         this.root = new Roo.form.Layout(Roo.applyIf({
44282             id: Roo.id()
44283         }, config));
44284     }
44285     this.active = this.root;
44286     /**
44287      * Array of all the buttons that have been added to this form via {@link addButton}
44288      * @type Array
44289      */
44290     this.buttons = [];
44291     this.allItems = [];
44292     this.addEvents({
44293         /**
44294          * @event clientvalidation
44295          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44296          * @param {Form} this
44297          * @param {Boolean} valid true if the form has passed client-side validation
44298          */
44299         clientvalidation: true,
44300         /**
44301          * @event rendered
44302          * Fires when the form is rendered
44303          * @param {Roo.form.Form} form
44304          */
44305         rendered : true
44306     });
44307     
44308     if (this.progressUrl) {
44309             // push a hidden field onto the list of fields..
44310             this.addxtype( {
44311                     xns: Roo.form, 
44312                     xtype : 'Hidden', 
44313                     name : 'UPLOAD_IDENTIFIER' 
44314             });
44315         }
44316         
44317     
44318     Roo.each(xitems, this.addxtype, this);
44319     
44320     
44321     
44322 };
44323
44324 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44325     /**
44326      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44327      */
44328     /**
44329      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44330      */
44331     /**
44332      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44333      */
44334     buttonAlign:'center',
44335
44336     /**
44337      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44338      */
44339     minButtonWidth:75,
44340
44341     /**
44342      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44343      * This property cascades to child containers if not set.
44344      */
44345     labelAlign:'left',
44346
44347     /**
44348      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44349      * fires a looping event with that state. This is required to bind buttons to the valid
44350      * state using the config value formBind:true on the button.
44351      */
44352     monitorValid : false,
44353
44354     /**
44355      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44356      */
44357     monitorPoll : 200,
44358     
44359     /**
44360      * @cfg {String} progressUrl - Url to return progress data 
44361      */
44362     
44363     progressUrl : false,
44364   
44365     /**
44366      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44367      * fields are added and the column is closed. If no fields are passed the column remains open
44368      * until end() is called.
44369      * @param {Object} config The config to pass to the column
44370      * @param {Field} field1 (optional)
44371      * @param {Field} field2 (optional)
44372      * @param {Field} etc (optional)
44373      * @return Column The column container object
44374      */
44375     column : function(c){
44376         var col = new Roo.form.Column(c);
44377         this.start(col);
44378         if(arguments.length > 1){ // duplicate code required because of Opera
44379             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44380             this.end();
44381         }
44382         return col;
44383     },
44384
44385     /**
44386      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44387      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44388      * until end() is called.
44389      * @param {Object} config The config to pass to the fieldset
44390      * @param {Field} field1 (optional)
44391      * @param {Field} field2 (optional)
44392      * @param {Field} etc (optional)
44393      * @return FieldSet The fieldset container object
44394      */
44395     fieldset : function(c){
44396         var fs = new Roo.form.FieldSet(c);
44397         this.start(fs);
44398         if(arguments.length > 1){ // duplicate code required because of Opera
44399             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44400             this.end();
44401         }
44402         return fs;
44403     },
44404
44405     /**
44406      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44407      * fields are added and the container is closed. If no fields are passed the container remains open
44408      * until end() is called.
44409      * @param {Object} config The config to pass to the Layout
44410      * @param {Field} field1 (optional)
44411      * @param {Field} field2 (optional)
44412      * @param {Field} etc (optional)
44413      * @return Layout The container object
44414      */
44415     container : function(c){
44416         var l = new Roo.form.Layout(c);
44417         this.start(l);
44418         if(arguments.length > 1){ // duplicate code required because of Opera
44419             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44420             this.end();
44421         }
44422         return l;
44423     },
44424
44425     /**
44426      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44427      * @param {Object} container A Roo.form.Layout or subclass of Layout
44428      * @return {Form} this
44429      */
44430     start : function(c){
44431         // cascade label info
44432         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44433         this.active.stack.push(c);
44434         c.ownerCt = this.active;
44435         this.active = c;
44436         return this;
44437     },
44438
44439     /**
44440      * Closes the current open container
44441      * @return {Form} this
44442      */
44443     end : function(){
44444         if(this.active == this.root){
44445             return this;
44446         }
44447         this.active = this.active.ownerCt;
44448         return this;
44449     },
44450
44451     /**
44452      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44453      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44454      * as the label of the field.
44455      * @param {Field} field1
44456      * @param {Field} field2 (optional)
44457      * @param {Field} etc. (optional)
44458      * @return {Form} this
44459      */
44460     add : function(){
44461         this.active.stack.push.apply(this.active.stack, arguments);
44462         this.allItems.push.apply(this.allItems,arguments);
44463         var r = [];
44464         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44465             if(a[i].isFormField){
44466                 r.push(a[i]);
44467             }
44468         }
44469         if(r.length > 0){
44470             Roo.form.Form.superclass.add.apply(this, r);
44471         }
44472         return this;
44473     },
44474     
44475
44476     
44477     
44478     
44479      /**
44480      * Find any element that has been added to a form, using it's ID or name
44481      * This can include framesets, columns etc. along with regular fields..
44482      * @param {String} id - id or name to find.
44483      
44484      * @return {Element} e - or false if nothing found.
44485      */
44486     findbyId : function(id)
44487     {
44488         var ret = false;
44489         if (!id) {
44490             return ret;
44491         }
44492         Roo.each(this.allItems, function(f){
44493             if (f.id == id || f.name == id ){
44494                 ret = f;
44495                 return false;
44496             }
44497         });
44498         return ret;
44499     },
44500
44501     
44502     
44503     /**
44504      * Render this form into the passed container. This should only be called once!
44505      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44506      * @return {Form} this
44507      */
44508     render : function(ct)
44509     {
44510         
44511         
44512         
44513         ct = Roo.get(ct);
44514         var o = this.autoCreate || {
44515             tag: 'form',
44516             method : this.method || 'POST',
44517             id : this.id || Roo.id()
44518         };
44519         this.initEl(ct.createChild(o));
44520
44521         this.root.render(this.el);
44522         
44523        
44524              
44525         this.items.each(function(f){
44526             f.render('x-form-el-'+f.id);
44527         });
44528
44529         if(this.buttons.length > 0){
44530             // tables are required to maintain order and for correct IE layout
44531             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44532                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44533                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44534             }}, null, true);
44535             var tr = tb.getElementsByTagName('tr')[0];
44536             for(var i = 0, len = this.buttons.length; i < len; i++) {
44537                 var b = this.buttons[i];
44538                 var td = document.createElement('td');
44539                 td.className = 'x-form-btn-td';
44540                 b.render(tr.appendChild(td));
44541             }
44542         }
44543         if(this.monitorValid){ // initialize after render
44544             this.startMonitoring();
44545         }
44546         this.fireEvent('rendered', this);
44547         return this;
44548     },
44549
44550     /**
44551      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44552      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44553      * object or a valid Roo.DomHelper element config
44554      * @param {Function} handler The function called when the button is clicked
44555      * @param {Object} scope (optional) The scope of the handler function
44556      * @return {Roo.Button}
44557      */
44558     addButton : function(config, handler, scope){
44559         var bc = {
44560             handler: handler,
44561             scope: scope,
44562             minWidth: this.minButtonWidth,
44563             hideParent:true
44564         };
44565         if(typeof config == "string"){
44566             bc.text = config;
44567         }else{
44568             Roo.apply(bc, config);
44569         }
44570         var btn = new Roo.Button(null, bc);
44571         this.buttons.push(btn);
44572         return btn;
44573     },
44574
44575      /**
44576      * Adds a series of form elements (using the xtype property as the factory method.
44577      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44578      * @param {Object} config 
44579      */
44580     
44581     addxtype : function()
44582     {
44583         var ar = Array.prototype.slice.call(arguments, 0);
44584         var ret = false;
44585         for(var i = 0; i < ar.length; i++) {
44586             if (!ar[i]) {
44587                 continue; // skip -- if this happends something invalid got sent, we 
44588                 // should ignore it, as basically that interface element will not show up
44589                 // and that should be pretty obvious!!
44590             }
44591             
44592             if (Roo.form[ar[i].xtype]) {
44593                 ar[i].form = this;
44594                 var fe = Roo.factory(ar[i], Roo.form);
44595                 if (!ret) {
44596                     ret = fe;
44597                 }
44598                 fe.form = this;
44599                 if (fe.store) {
44600                     fe.store.form = this;
44601                 }
44602                 if (fe.isLayout) {  
44603                          
44604                     this.start(fe);
44605                     this.allItems.push(fe);
44606                     if (fe.items && fe.addxtype) {
44607                         fe.addxtype.apply(fe, fe.items);
44608                         delete fe.items;
44609                     }
44610                      this.end();
44611                     continue;
44612                 }
44613                 
44614                 
44615                  
44616                 this.add(fe);
44617               //  console.log('adding ' + ar[i].xtype);
44618             }
44619             if (ar[i].xtype == 'Button') {  
44620                 //console.log('adding button');
44621                 //console.log(ar[i]);
44622                 this.addButton(ar[i]);
44623                 this.allItems.push(fe);
44624                 continue;
44625             }
44626             
44627             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44628                 alert('end is not supported on xtype any more, use items');
44629             //    this.end();
44630             //    //console.log('adding end');
44631             }
44632             
44633         }
44634         return ret;
44635     },
44636     
44637     /**
44638      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44639      * option "monitorValid"
44640      */
44641     startMonitoring : function(){
44642         if(!this.bound){
44643             this.bound = true;
44644             Roo.TaskMgr.start({
44645                 run : this.bindHandler,
44646                 interval : this.monitorPoll || 200,
44647                 scope: this
44648             });
44649         }
44650     },
44651
44652     /**
44653      * Stops monitoring of the valid state of this form
44654      */
44655     stopMonitoring : function(){
44656         this.bound = false;
44657     },
44658
44659     // private
44660     bindHandler : function(){
44661         if(!this.bound){
44662             return false; // stops binding
44663         }
44664         var valid = true;
44665         this.items.each(function(f){
44666             if(!f.isValid(true)){
44667                 valid = false;
44668                 return false;
44669             }
44670         });
44671         for(var i = 0, len = this.buttons.length; i < len; i++){
44672             var btn = this.buttons[i];
44673             if(btn.formBind === true && btn.disabled === valid){
44674                 btn.setDisabled(!valid);
44675             }
44676         }
44677         this.fireEvent('clientvalidation', this, valid);
44678     }
44679     
44680     
44681     
44682     
44683     
44684     
44685     
44686     
44687 });
44688
44689
44690 // back compat
44691 Roo.Form = Roo.form.Form;
44692 /*
44693  * Based on:
44694  * Ext JS Library 1.1.1
44695  * Copyright(c) 2006-2007, Ext JS, LLC.
44696  *
44697  * Originally Released Under LGPL - original licence link has changed is not relivant.
44698  *
44699  * Fork - LGPL
44700  * <script type="text/javascript">
44701  */
44702
44703 // as we use this in bootstrap.
44704 Roo.namespace('Roo.form');
44705  /**
44706  * @class Roo.form.Action
44707  * Internal Class used to handle form actions
44708  * @constructor
44709  * @param {Roo.form.BasicForm} el The form element or its id
44710  * @param {Object} config Configuration options
44711  */
44712
44713  
44714  
44715 // define the action interface
44716 Roo.form.Action = function(form, options){
44717     this.form = form;
44718     this.options = options || {};
44719 };
44720 /**
44721  * Client Validation Failed
44722  * @const 
44723  */
44724 Roo.form.Action.CLIENT_INVALID = 'client';
44725 /**
44726  * Server Validation Failed
44727  * @const 
44728  */
44729 Roo.form.Action.SERVER_INVALID = 'server';
44730  /**
44731  * Connect to Server Failed
44732  * @const 
44733  */
44734 Roo.form.Action.CONNECT_FAILURE = 'connect';
44735 /**
44736  * Reading Data from Server Failed
44737  * @const 
44738  */
44739 Roo.form.Action.LOAD_FAILURE = 'load';
44740
44741 Roo.form.Action.prototype = {
44742     type : 'default',
44743     failureType : undefined,
44744     response : undefined,
44745     result : undefined,
44746
44747     // interface method
44748     run : function(options){
44749
44750     },
44751
44752     // interface method
44753     success : function(response){
44754
44755     },
44756
44757     // interface method
44758     handleResponse : function(response){
44759
44760     },
44761
44762     // default connection failure
44763     failure : function(response){
44764         
44765         this.response = response;
44766         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44767         this.form.afterAction(this, false);
44768     },
44769
44770     processResponse : function(response){
44771         this.response = response;
44772         if(!response.responseText){
44773             return true;
44774         }
44775         this.result = this.handleResponse(response);
44776         return this.result;
44777     },
44778
44779     // utility functions used internally
44780     getUrl : function(appendParams){
44781         var url = this.options.url || this.form.url || this.form.el.dom.action;
44782         if(appendParams){
44783             var p = this.getParams();
44784             if(p){
44785                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44786             }
44787         }
44788         return url;
44789     },
44790
44791     getMethod : function(){
44792         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44793     },
44794
44795     getParams : function(){
44796         var bp = this.form.baseParams;
44797         var p = this.options.params;
44798         if(p){
44799             if(typeof p == "object"){
44800                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44801             }else if(typeof p == 'string' && bp){
44802                 p += '&' + Roo.urlEncode(bp);
44803             }
44804         }else if(bp){
44805             p = Roo.urlEncode(bp);
44806         }
44807         return p;
44808     },
44809
44810     createCallback : function(){
44811         return {
44812             success: this.success,
44813             failure: this.failure,
44814             scope: this,
44815             timeout: (this.form.timeout*1000),
44816             upload: this.form.fileUpload ? this.success : undefined
44817         };
44818     }
44819 };
44820
44821 Roo.form.Action.Submit = function(form, options){
44822     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44823 };
44824
44825 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44826     type : 'submit',
44827
44828     haveProgress : false,
44829     uploadComplete : false,
44830     
44831     // uploadProgress indicator.
44832     uploadProgress : function()
44833     {
44834         if (!this.form.progressUrl) {
44835             return;
44836         }
44837         
44838         if (!this.haveProgress) {
44839             Roo.MessageBox.progress("Uploading", "Uploading");
44840         }
44841         if (this.uploadComplete) {
44842            Roo.MessageBox.hide();
44843            return;
44844         }
44845         
44846         this.haveProgress = true;
44847    
44848         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44849         
44850         var c = new Roo.data.Connection();
44851         c.request({
44852             url : this.form.progressUrl,
44853             params: {
44854                 id : uid
44855             },
44856             method: 'GET',
44857             success : function(req){
44858                //console.log(data);
44859                 var rdata = false;
44860                 var edata;
44861                 try  {
44862                    rdata = Roo.decode(req.responseText)
44863                 } catch (e) {
44864                     Roo.log("Invalid data from server..");
44865                     Roo.log(edata);
44866                     return;
44867                 }
44868                 if (!rdata || !rdata.success) {
44869                     Roo.log(rdata);
44870                     Roo.MessageBox.alert(Roo.encode(rdata));
44871                     return;
44872                 }
44873                 var data = rdata.data;
44874                 
44875                 if (this.uploadComplete) {
44876                    Roo.MessageBox.hide();
44877                    return;
44878                 }
44879                    
44880                 if (data){
44881                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44882                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44883                     );
44884                 }
44885                 this.uploadProgress.defer(2000,this);
44886             },
44887        
44888             failure: function(data) {
44889                 Roo.log('progress url failed ');
44890                 Roo.log(data);
44891             },
44892             scope : this
44893         });
44894            
44895     },
44896     
44897     
44898     run : function()
44899     {
44900         // run get Values on the form, so it syncs any secondary forms.
44901         this.form.getValues();
44902         
44903         var o = this.options;
44904         var method = this.getMethod();
44905         var isPost = method == 'POST';
44906         if(o.clientValidation === false || this.form.isValid()){
44907             
44908             if (this.form.progressUrl) {
44909                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44910                     (new Date() * 1) + '' + Math.random());
44911                     
44912             } 
44913             
44914             
44915             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44916                 form:this.form.el.dom,
44917                 url:this.getUrl(!isPost),
44918                 method: method,
44919                 params:isPost ? this.getParams() : null,
44920                 isUpload: this.form.fileUpload
44921             }));
44922             
44923             this.uploadProgress();
44924
44925         }else if (o.clientValidation !== false){ // client validation failed
44926             this.failureType = Roo.form.Action.CLIENT_INVALID;
44927             this.form.afterAction(this, false);
44928         }
44929     },
44930
44931     success : function(response)
44932     {
44933         this.uploadComplete= true;
44934         if (this.haveProgress) {
44935             Roo.MessageBox.hide();
44936         }
44937         
44938         
44939         var result = this.processResponse(response);
44940         if(result === true || result.success){
44941             this.form.afterAction(this, true);
44942             return;
44943         }
44944         if(result.errors){
44945             this.form.markInvalid(result.errors);
44946             this.failureType = Roo.form.Action.SERVER_INVALID;
44947         }
44948         this.form.afterAction(this, false);
44949     },
44950     failure : function(response)
44951     {
44952         this.uploadComplete= true;
44953         if (this.haveProgress) {
44954             Roo.MessageBox.hide();
44955         }
44956         
44957         this.response = response;
44958         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44959         this.form.afterAction(this, false);
44960     },
44961     
44962     handleResponse : function(response){
44963         if(this.form.errorReader){
44964             var rs = this.form.errorReader.read(response);
44965             var errors = [];
44966             if(rs.records){
44967                 for(var i = 0, len = rs.records.length; i < len; i++) {
44968                     var r = rs.records[i];
44969                     errors[i] = r.data;
44970                 }
44971             }
44972             if(errors.length < 1){
44973                 errors = null;
44974             }
44975             return {
44976                 success : rs.success,
44977                 errors : errors
44978             };
44979         }
44980         var ret = false;
44981         try {
44982             ret = Roo.decode(response.responseText);
44983         } catch (e) {
44984             ret = {
44985                 success: false,
44986                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44987                 errors : []
44988             };
44989         }
44990         return ret;
44991         
44992     }
44993 });
44994
44995
44996 Roo.form.Action.Load = function(form, options){
44997     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44998     this.reader = this.form.reader;
44999 };
45000
45001 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45002     type : 'load',
45003
45004     run : function(){
45005         
45006         Roo.Ajax.request(Roo.apply(
45007                 this.createCallback(), {
45008                     method:this.getMethod(),
45009                     url:this.getUrl(false),
45010                     params:this.getParams()
45011         }));
45012     },
45013
45014     success : function(response){
45015         
45016         var result = this.processResponse(response);
45017         if(result === true || !result.success || !result.data){
45018             this.failureType = Roo.form.Action.LOAD_FAILURE;
45019             this.form.afterAction(this, false);
45020             return;
45021         }
45022         this.form.clearInvalid();
45023         this.form.setValues(result.data);
45024         this.form.afterAction(this, true);
45025     },
45026
45027     handleResponse : function(response){
45028         if(this.form.reader){
45029             var rs = this.form.reader.read(response);
45030             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45031             return {
45032                 success : rs.success,
45033                 data : data
45034             };
45035         }
45036         return Roo.decode(response.responseText);
45037     }
45038 });
45039
45040 Roo.form.Action.ACTION_TYPES = {
45041     'load' : Roo.form.Action.Load,
45042     'submit' : Roo.form.Action.Submit
45043 };/*
45044  * Based on:
45045  * Ext JS Library 1.1.1
45046  * Copyright(c) 2006-2007, Ext JS, LLC.
45047  *
45048  * Originally Released Under LGPL - original licence link has changed is not relivant.
45049  *
45050  * Fork - LGPL
45051  * <script type="text/javascript">
45052  */
45053  
45054 /**
45055  * @class Roo.form.Layout
45056  * @extends Roo.Component
45057  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45058  * @constructor
45059  * @param {Object} config Configuration options
45060  */
45061 Roo.form.Layout = function(config){
45062     var xitems = [];
45063     if (config.items) {
45064         xitems = config.items;
45065         delete config.items;
45066     }
45067     Roo.form.Layout.superclass.constructor.call(this, config);
45068     this.stack = [];
45069     Roo.each(xitems, this.addxtype, this);
45070      
45071 };
45072
45073 Roo.extend(Roo.form.Layout, Roo.Component, {
45074     /**
45075      * @cfg {String/Object} autoCreate
45076      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45077      */
45078     /**
45079      * @cfg {String/Object/Function} style
45080      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45081      * a function which returns such a specification.
45082      */
45083     /**
45084      * @cfg {String} labelAlign
45085      * Valid values are "left," "top" and "right" (defaults to "left")
45086      */
45087     /**
45088      * @cfg {Number} labelWidth
45089      * Fixed width in pixels of all field labels (defaults to undefined)
45090      */
45091     /**
45092      * @cfg {Boolean} clear
45093      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45094      */
45095     clear : true,
45096     /**
45097      * @cfg {String} labelSeparator
45098      * The separator to use after field labels (defaults to ':')
45099      */
45100     labelSeparator : ':',
45101     /**
45102      * @cfg {Boolean} hideLabels
45103      * True to suppress the display of field labels in this layout (defaults to false)
45104      */
45105     hideLabels : false,
45106
45107     // private
45108     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45109     
45110     isLayout : true,
45111     
45112     // private
45113     onRender : function(ct, position){
45114         if(this.el){ // from markup
45115             this.el = Roo.get(this.el);
45116         }else {  // generate
45117             var cfg = this.getAutoCreate();
45118             this.el = ct.createChild(cfg, position);
45119         }
45120         if(this.style){
45121             this.el.applyStyles(this.style);
45122         }
45123         if(this.labelAlign){
45124             this.el.addClass('x-form-label-'+this.labelAlign);
45125         }
45126         if(this.hideLabels){
45127             this.labelStyle = "display:none";
45128             this.elementStyle = "padding-left:0;";
45129         }else{
45130             if(typeof this.labelWidth == 'number'){
45131                 this.labelStyle = "width:"+this.labelWidth+"px;";
45132                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45133             }
45134             if(this.labelAlign == 'top'){
45135                 this.labelStyle = "width:auto;";
45136                 this.elementStyle = "padding-left:0;";
45137             }
45138         }
45139         var stack = this.stack;
45140         var slen = stack.length;
45141         if(slen > 0){
45142             if(!this.fieldTpl){
45143                 var t = new Roo.Template(
45144                     '<div class="x-form-item {5}">',
45145                         '<label for="{0}" style="{2}">{1}{4}</label>',
45146                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45147                         '</div>',
45148                     '</div><div class="x-form-clear-left"></div>'
45149                 );
45150                 t.disableFormats = true;
45151                 t.compile();
45152                 Roo.form.Layout.prototype.fieldTpl = t;
45153             }
45154             for(var i = 0; i < slen; i++) {
45155                 if(stack[i].isFormField){
45156                     this.renderField(stack[i]);
45157                 }else{
45158                     this.renderComponent(stack[i]);
45159                 }
45160             }
45161         }
45162         if(this.clear){
45163             this.el.createChild({cls:'x-form-clear'});
45164         }
45165     },
45166
45167     // private
45168     renderField : function(f){
45169         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45170                f.id, //0
45171                f.fieldLabel, //1
45172                f.labelStyle||this.labelStyle||'', //2
45173                this.elementStyle||'', //3
45174                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45175                f.itemCls||this.itemCls||''  //5
45176        ], true).getPrevSibling());
45177     },
45178
45179     // private
45180     renderComponent : function(c){
45181         c.render(c.isLayout ? this.el : this.el.createChild());    
45182     },
45183     /**
45184      * Adds a object form elements (using the xtype property as the factory method.)
45185      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45186      * @param {Object} config 
45187      */
45188     addxtype : function(o)
45189     {
45190         // create the lement.
45191         o.form = this.form;
45192         var fe = Roo.factory(o, Roo.form);
45193         this.form.allItems.push(fe);
45194         this.stack.push(fe);
45195         
45196         if (fe.isFormField) {
45197             this.form.items.add(fe);
45198         }
45199          
45200         return fe;
45201     }
45202 });
45203
45204 /**
45205  * @class Roo.form.Column
45206  * @extends Roo.form.Layout
45207  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45208  * @constructor
45209  * @param {Object} config Configuration options
45210  */
45211 Roo.form.Column = function(config){
45212     Roo.form.Column.superclass.constructor.call(this, config);
45213 };
45214
45215 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45216     /**
45217      * @cfg {Number/String} width
45218      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45219      */
45220     /**
45221      * @cfg {String/Object} autoCreate
45222      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45223      */
45224
45225     // private
45226     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45227
45228     // private
45229     onRender : function(ct, position){
45230         Roo.form.Column.superclass.onRender.call(this, ct, position);
45231         if(this.width){
45232             this.el.setWidth(this.width);
45233         }
45234     }
45235 });
45236
45237
45238 /**
45239  * @class Roo.form.Row
45240  * @extends Roo.form.Layout
45241  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45242  * @constructor
45243  * @param {Object} config Configuration options
45244  */
45245
45246  
45247 Roo.form.Row = function(config){
45248     Roo.form.Row.superclass.constructor.call(this, config);
45249 };
45250  
45251 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45252       /**
45253      * @cfg {Number/String} width
45254      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45255      */
45256     /**
45257      * @cfg {Number/String} height
45258      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45259      */
45260     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45261     
45262     padWidth : 20,
45263     // private
45264     onRender : function(ct, position){
45265         //console.log('row render');
45266         if(!this.rowTpl){
45267             var t = new Roo.Template(
45268                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45269                     '<label for="{0}" style="{2}">{1}{4}</label>',
45270                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45271                     '</div>',
45272                 '</div>'
45273             );
45274             t.disableFormats = true;
45275             t.compile();
45276             Roo.form.Layout.prototype.rowTpl = t;
45277         }
45278         this.fieldTpl = this.rowTpl;
45279         
45280         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45281         var labelWidth = 100;
45282         
45283         if ((this.labelAlign != 'top')) {
45284             if (typeof this.labelWidth == 'number') {
45285                 labelWidth = this.labelWidth
45286             }
45287             this.padWidth =  20 + labelWidth;
45288             
45289         }
45290         
45291         Roo.form.Column.superclass.onRender.call(this, ct, position);
45292         if(this.width){
45293             this.el.setWidth(this.width);
45294         }
45295         if(this.height){
45296             this.el.setHeight(this.height);
45297         }
45298     },
45299     
45300     // private
45301     renderField : function(f){
45302         f.fieldEl = this.fieldTpl.append(this.el, [
45303                f.id, f.fieldLabel,
45304                f.labelStyle||this.labelStyle||'',
45305                this.elementStyle||'',
45306                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45307                f.itemCls||this.itemCls||'',
45308                f.width ? f.width + this.padWidth : 160 + this.padWidth
45309        ],true);
45310     }
45311 });
45312  
45313
45314 /**
45315  * @class Roo.form.FieldSet
45316  * @extends Roo.form.Layout
45317  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45318  * @constructor
45319  * @param {Object} config Configuration options
45320  */
45321 Roo.form.FieldSet = function(config){
45322     Roo.form.FieldSet.superclass.constructor.call(this, config);
45323 };
45324
45325 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45326     /**
45327      * @cfg {String} legend
45328      * The text to display as the legend for the FieldSet (defaults to '')
45329      */
45330     /**
45331      * @cfg {String/Object} autoCreate
45332      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45333      */
45334
45335     // private
45336     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45337
45338     // private
45339     onRender : function(ct, position){
45340         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45341         if(this.legend){
45342             this.setLegend(this.legend);
45343         }
45344     },
45345
45346     // private
45347     setLegend : function(text){
45348         if(this.rendered){
45349             this.el.child('legend').update(text);
45350         }
45351     }
45352 });/*
45353  * Based on:
45354  * Ext JS Library 1.1.1
45355  * Copyright(c) 2006-2007, Ext JS, LLC.
45356  *
45357  * Originally Released Under LGPL - original licence link has changed is not relivant.
45358  *
45359  * Fork - LGPL
45360  * <script type="text/javascript">
45361  */
45362 /**
45363  * @class Roo.form.VTypes
45364  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45365  * @singleton
45366  */
45367 Roo.form.VTypes = function(){
45368     // closure these in so they are only created once.
45369     var alpha = /^[a-zA-Z_]+$/;
45370     var alphanum = /^[a-zA-Z0-9_]+$/;
45371     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45372     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45373
45374     // All these messages and functions are configurable
45375     return {
45376         /**
45377          * The function used to validate email addresses
45378          * @param {String} value The email address
45379          */
45380         'email' : function(v){
45381             return email.test(v);
45382         },
45383         /**
45384          * The error text to display when the email validation function returns false
45385          * @type String
45386          */
45387         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45388         /**
45389          * The keystroke filter mask to be applied on email input
45390          * @type RegExp
45391          */
45392         'emailMask' : /[a-z0-9_\.\-@]/i,
45393
45394         /**
45395          * The function used to validate URLs
45396          * @param {String} value The URL
45397          */
45398         'url' : function(v){
45399             return url.test(v);
45400         },
45401         /**
45402          * The error text to display when the url validation function returns false
45403          * @type String
45404          */
45405         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45406         
45407         /**
45408          * The function used to validate alpha values
45409          * @param {String} value The value
45410          */
45411         'alpha' : function(v){
45412             return alpha.test(v);
45413         },
45414         /**
45415          * The error text to display when the alpha validation function returns false
45416          * @type String
45417          */
45418         'alphaText' : 'This field should only contain letters and _',
45419         /**
45420          * The keystroke filter mask to be applied on alpha input
45421          * @type RegExp
45422          */
45423         'alphaMask' : /[a-z_]/i,
45424
45425         /**
45426          * The function used to validate alphanumeric values
45427          * @param {String} value The value
45428          */
45429         'alphanum' : function(v){
45430             return alphanum.test(v);
45431         },
45432         /**
45433          * The error text to display when the alphanumeric validation function returns false
45434          * @type String
45435          */
45436         'alphanumText' : 'This field should only contain letters, numbers and _',
45437         /**
45438          * The keystroke filter mask to be applied on alphanumeric input
45439          * @type RegExp
45440          */
45441         'alphanumMask' : /[a-z0-9_]/i
45442     };
45443 }();//<script type="text/javascript">
45444
45445 /**
45446  * @class Roo.form.FCKeditor
45447  * @extends Roo.form.TextArea
45448  * Wrapper around the FCKEditor http://www.fckeditor.net
45449  * @constructor
45450  * Creates a new FCKeditor
45451  * @param {Object} config Configuration options
45452  */
45453 Roo.form.FCKeditor = function(config){
45454     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45455     this.addEvents({
45456          /**
45457          * @event editorinit
45458          * Fired when the editor is initialized - you can add extra handlers here..
45459          * @param {FCKeditor} this
45460          * @param {Object} the FCK object.
45461          */
45462         editorinit : true
45463     });
45464     
45465     
45466 };
45467 Roo.form.FCKeditor.editors = { };
45468 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45469 {
45470     //defaultAutoCreate : {
45471     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45472     //},
45473     // private
45474     /**
45475      * @cfg {Object} fck options - see fck manual for details.
45476      */
45477     fckconfig : false,
45478     
45479     /**
45480      * @cfg {Object} fck toolbar set (Basic or Default)
45481      */
45482     toolbarSet : 'Basic',
45483     /**
45484      * @cfg {Object} fck BasePath
45485      */ 
45486     basePath : '/fckeditor/',
45487     
45488     
45489     frame : false,
45490     
45491     value : '',
45492     
45493    
45494     onRender : function(ct, position)
45495     {
45496         if(!this.el){
45497             this.defaultAutoCreate = {
45498                 tag: "textarea",
45499                 style:"width:300px;height:60px;",
45500                 autocomplete: "off"
45501             };
45502         }
45503         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45504         /*
45505         if(this.grow){
45506             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45507             if(this.preventScrollbars){
45508                 this.el.setStyle("overflow", "hidden");
45509             }
45510             this.el.setHeight(this.growMin);
45511         }
45512         */
45513         //console.log('onrender' + this.getId() );
45514         Roo.form.FCKeditor.editors[this.getId()] = this;
45515          
45516
45517         this.replaceTextarea() ;
45518         
45519     },
45520     
45521     getEditor : function() {
45522         return this.fckEditor;
45523     },
45524     /**
45525      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45526      * @param {Mixed} value The value to set
45527      */
45528     
45529     
45530     setValue : function(value)
45531     {
45532         //console.log('setValue: ' + value);
45533         
45534         if(typeof(value) == 'undefined') { // not sure why this is happending...
45535             return;
45536         }
45537         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45538         
45539         //if(!this.el || !this.getEditor()) {
45540         //    this.value = value;
45541             //this.setValue.defer(100,this,[value]);    
45542         //    return;
45543         //} 
45544         
45545         if(!this.getEditor()) {
45546             return;
45547         }
45548         
45549         this.getEditor().SetData(value);
45550         
45551         //
45552
45553     },
45554
45555     /**
45556      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45557      * @return {Mixed} value The field value
45558      */
45559     getValue : function()
45560     {
45561         
45562         if (this.frame && this.frame.dom.style.display == 'none') {
45563             return Roo.form.FCKeditor.superclass.getValue.call(this);
45564         }
45565         
45566         if(!this.el || !this.getEditor()) {
45567            
45568            // this.getValue.defer(100,this); 
45569             return this.value;
45570         }
45571        
45572         
45573         var value=this.getEditor().GetData();
45574         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45575         return Roo.form.FCKeditor.superclass.getValue.call(this);
45576         
45577
45578     },
45579
45580     /**
45581      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45582      * @return {Mixed} value The field value
45583      */
45584     getRawValue : function()
45585     {
45586         if (this.frame && this.frame.dom.style.display == 'none') {
45587             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45588         }
45589         
45590         if(!this.el || !this.getEditor()) {
45591             //this.getRawValue.defer(100,this); 
45592             return this.value;
45593             return;
45594         }
45595         
45596         
45597         
45598         var value=this.getEditor().GetData();
45599         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45600         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45601          
45602     },
45603     
45604     setSize : function(w,h) {
45605         
45606         
45607         
45608         //if (this.frame && this.frame.dom.style.display == 'none') {
45609         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45610         //    return;
45611         //}
45612         //if(!this.el || !this.getEditor()) {
45613         //    this.setSize.defer(100,this, [w,h]); 
45614         //    return;
45615         //}
45616         
45617         
45618         
45619         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45620         
45621         this.frame.dom.setAttribute('width', w);
45622         this.frame.dom.setAttribute('height', h);
45623         this.frame.setSize(w,h);
45624         
45625     },
45626     
45627     toggleSourceEdit : function(value) {
45628         
45629       
45630          
45631         this.el.dom.style.display = value ? '' : 'none';
45632         this.frame.dom.style.display = value ?  'none' : '';
45633         
45634     },
45635     
45636     
45637     focus: function(tag)
45638     {
45639         if (this.frame.dom.style.display == 'none') {
45640             return Roo.form.FCKeditor.superclass.focus.call(this);
45641         }
45642         if(!this.el || !this.getEditor()) {
45643             this.focus.defer(100,this, [tag]); 
45644             return;
45645         }
45646         
45647         
45648         
45649         
45650         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45651         this.getEditor().Focus();
45652         if (tgs.length) {
45653             if (!this.getEditor().Selection.GetSelection()) {
45654                 this.focus.defer(100,this, [tag]); 
45655                 return;
45656             }
45657             
45658             
45659             var r = this.getEditor().EditorDocument.createRange();
45660             r.setStart(tgs[0],0);
45661             r.setEnd(tgs[0],0);
45662             this.getEditor().Selection.GetSelection().removeAllRanges();
45663             this.getEditor().Selection.GetSelection().addRange(r);
45664             this.getEditor().Focus();
45665         }
45666         
45667     },
45668     
45669     
45670     
45671     replaceTextarea : function()
45672     {
45673         if ( document.getElementById( this.getId() + '___Frame' ) )
45674             return ;
45675         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45676         //{
45677             // We must check the elements firstly using the Id and then the name.
45678         var oTextarea = document.getElementById( this.getId() );
45679         
45680         var colElementsByName = document.getElementsByName( this.getId() ) ;
45681          
45682         oTextarea.style.display = 'none' ;
45683
45684         if ( oTextarea.tabIndex ) {            
45685             this.TabIndex = oTextarea.tabIndex ;
45686         }
45687         
45688         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45689         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45690         this.frame = Roo.get(this.getId() + '___Frame')
45691     },
45692     
45693     _getConfigHtml : function()
45694     {
45695         var sConfig = '' ;
45696
45697         for ( var o in this.fckconfig ) {
45698             sConfig += sConfig.length > 0  ? '&amp;' : '';
45699             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45700         }
45701
45702         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45703     },
45704     
45705     
45706     _getIFrameHtml : function()
45707     {
45708         var sFile = 'fckeditor.html' ;
45709         /* no idea what this is about..
45710         try
45711         {
45712             if ( (/fcksource=true/i).test( window.top.location.search ) )
45713                 sFile = 'fckeditor.original.html' ;
45714         }
45715         catch (e) { 
45716         */
45717
45718         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45719         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45720         
45721         
45722         var html = '<iframe id="' + this.getId() +
45723             '___Frame" src="' + sLink +
45724             '" width="' + this.width +
45725             '" height="' + this.height + '"' +
45726             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45727             ' frameborder="0" scrolling="no"></iframe>' ;
45728
45729         return html ;
45730     },
45731     
45732     _insertHtmlBefore : function( html, element )
45733     {
45734         if ( element.insertAdjacentHTML )       {
45735             // IE
45736             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45737         } else { // Gecko
45738             var oRange = document.createRange() ;
45739             oRange.setStartBefore( element ) ;
45740             var oFragment = oRange.createContextualFragment( html );
45741             element.parentNode.insertBefore( oFragment, element ) ;
45742         }
45743     }
45744     
45745     
45746   
45747     
45748     
45749     
45750     
45751
45752 });
45753
45754 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45755
45756 function FCKeditor_OnComplete(editorInstance){
45757     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45758     f.fckEditor = editorInstance;
45759     //console.log("loaded");
45760     f.fireEvent('editorinit', f, editorInstance);
45761
45762   
45763
45764  
45765
45766
45767
45768
45769
45770
45771
45772
45773
45774
45775
45776
45777
45778
45779
45780 //<script type="text/javascript">
45781 /**
45782  * @class Roo.form.GridField
45783  * @extends Roo.form.Field
45784  * Embed a grid (or editable grid into a form)
45785  * STATUS ALPHA
45786  * 
45787  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45788  * it needs 
45789  * xgrid.store = Roo.data.Store
45790  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45791  * xgrid.store.reader = Roo.data.JsonReader 
45792  * 
45793  * 
45794  * @constructor
45795  * Creates a new GridField
45796  * @param {Object} config Configuration options
45797  */
45798 Roo.form.GridField = function(config){
45799     Roo.form.GridField.superclass.constructor.call(this, config);
45800      
45801 };
45802
45803 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45804     /**
45805      * @cfg {Number} width  - used to restrict width of grid..
45806      */
45807     width : 100,
45808     /**
45809      * @cfg {Number} height - used to restrict height of grid..
45810      */
45811     height : 50,
45812      /**
45813      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45814          * 
45815          *}
45816      */
45817     xgrid : false, 
45818     /**
45819      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45820      * {tag: "input", type: "checkbox", autocomplete: "off"})
45821      */
45822    // defaultAutoCreate : { tag: 'div' },
45823     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45824     /**
45825      * @cfg {String} addTitle Text to include for adding a title.
45826      */
45827     addTitle : false,
45828     //
45829     onResize : function(){
45830         Roo.form.Field.superclass.onResize.apply(this, arguments);
45831     },
45832
45833     initEvents : function(){
45834         // Roo.form.Checkbox.superclass.initEvents.call(this);
45835         // has no events...
45836        
45837     },
45838
45839
45840     getResizeEl : function(){
45841         return this.wrap;
45842     },
45843
45844     getPositionEl : function(){
45845         return this.wrap;
45846     },
45847
45848     // private
45849     onRender : function(ct, position){
45850         
45851         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45852         var style = this.style;
45853         delete this.style;
45854         
45855         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45856         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45857         this.viewEl = this.wrap.createChild({ tag: 'div' });
45858         if (style) {
45859             this.viewEl.applyStyles(style);
45860         }
45861         if (this.width) {
45862             this.viewEl.setWidth(this.width);
45863         }
45864         if (this.height) {
45865             this.viewEl.setHeight(this.height);
45866         }
45867         //if(this.inputValue !== undefined){
45868         //this.setValue(this.value);
45869         
45870         
45871         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45872         
45873         
45874         this.grid.render();
45875         this.grid.getDataSource().on('remove', this.refreshValue, this);
45876         this.grid.getDataSource().on('update', this.refreshValue, this);
45877         this.grid.on('afteredit', this.refreshValue, this);
45878  
45879     },
45880      
45881     
45882     /**
45883      * Sets the value of the item. 
45884      * @param {String} either an object  or a string..
45885      */
45886     setValue : function(v){
45887         //this.value = v;
45888         v = v || []; // empty set..
45889         // this does not seem smart - it really only affects memoryproxy grids..
45890         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45891             var ds = this.grid.getDataSource();
45892             // assumes a json reader..
45893             var data = {}
45894             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45895             ds.loadData( data);
45896         }
45897         // clear selection so it does not get stale.
45898         if (this.grid.sm) { 
45899             this.grid.sm.clearSelections();
45900         }
45901         
45902         Roo.form.GridField.superclass.setValue.call(this, v);
45903         this.refreshValue();
45904         // should load data in the grid really....
45905     },
45906     
45907     // private
45908     refreshValue: function() {
45909          var val = [];
45910         this.grid.getDataSource().each(function(r) {
45911             val.push(r.data);
45912         });
45913         this.el.dom.value = Roo.encode(val);
45914     }
45915     
45916      
45917     
45918     
45919 });/*
45920  * Based on:
45921  * Ext JS Library 1.1.1
45922  * Copyright(c) 2006-2007, Ext JS, LLC.
45923  *
45924  * Originally Released Under LGPL - original licence link has changed is not relivant.
45925  *
45926  * Fork - LGPL
45927  * <script type="text/javascript">
45928  */
45929 /**
45930  * @class Roo.form.DisplayField
45931  * @extends Roo.form.Field
45932  * A generic Field to display non-editable data.
45933  * @constructor
45934  * Creates a new Display Field item.
45935  * @param {Object} config Configuration options
45936  */
45937 Roo.form.DisplayField = function(config){
45938     Roo.form.DisplayField.superclass.constructor.call(this, config);
45939     
45940 };
45941
45942 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45943     inputType:      'hidden',
45944     allowBlank:     true,
45945     readOnly:         true,
45946     
45947  
45948     /**
45949      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45950      */
45951     focusClass : undefined,
45952     /**
45953      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45954      */
45955     fieldClass: 'x-form-field',
45956     
45957      /**
45958      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45959      */
45960     valueRenderer: undefined,
45961     
45962     width: 100,
45963     /**
45964      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45965      * {tag: "input", type: "checkbox", autocomplete: "off"})
45966      */
45967      
45968  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45969
45970     onResize : function(){
45971         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45972         
45973     },
45974
45975     initEvents : function(){
45976         // Roo.form.Checkbox.superclass.initEvents.call(this);
45977         // has no events...
45978        
45979     },
45980
45981
45982     getResizeEl : function(){
45983         return this.wrap;
45984     },
45985
45986     getPositionEl : function(){
45987         return this.wrap;
45988     },
45989
45990     // private
45991     onRender : function(ct, position){
45992         
45993         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45994         //if(this.inputValue !== undefined){
45995         this.wrap = this.el.wrap();
45996         
45997         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45998         
45999         if (this.bodyStyle) {
46000             this.viewEl.applyStyles(this.bodyStyle);
46001         }
46002         //this.viewEl.setStyle('padding', '2px');
46003         
46004         this.setValue(this.value);
46005         
46006     },
46007 /*
46008     // private
46009     initValue : Roo.emptyFn,
46010
46011   */
46012
46013         // private
46014     onClick : function(){
46015         
46016     },
46017
46018     /**
46019      * Sets the checked state of the checkbox.
46020      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46021      */
46022     setValue : function(v){
46023         this.value = v;
46024         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46025         // this might be called before we have a dom element..
46026         if (!this.viewEl) {
46027             return;
46028         }
46029         this.viewEl.dom.innerHTML = html;
46030         Roo.form.DisplayField.superclass.setValue.call(this, v);
46031
46032     }
46033 });/*
46034  * 
46035  * Licence- LGPL
46036  * 
46037  */
46038
46039 /**
46040  * @class Roo.form.DayPicker
46041  * @extends Roo.form.Field
46042  * A Day picker show [M] [T] [W] ....
46043  * @constructor
46044  * Creates a new Day Picker
46045  * @param {Object} config Configuration options
46046  */
46047 Roo.form.DayPicker= function(config){
46048     Roo.form.DayPicker.superclass.constructor.call(this, config);
46049      
46050 };
46051
46052 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46053     /**
46054      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46055      */
46056     focusClass : undefined,
46057     /**
46058      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46059      */
46060     fieldClass: "x-form-field",
46061    
46062     /**
46063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46064      * {tag: "input", type: "checkbox", autocomplete: "off"})
46065      */
46066     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46067     
46068    
46069     actionMode : 'viewEl', 
46070     //
46071     // private
46072  
46073     inputType : 'hidden',
46074     
46075      
46076     inputElement: false, // real input element?
46077     basedOn: false, // ????
46078     
46079     isFormField: true, // not sure where this is needed!!!!
46080
46081     onResize : function(){
46082         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46083         if(!this.boxLabel){
46084             this.el.alignTo(this.wrap, 'c-c');
46085         }
46086     },
46087
46088     initEvents : function(){
46089         Roo.form.Checkbox.superclass.initEvents.call(this);
46090         this.el.on("click", this.onClick,  this);
46091         this.el.on("change", this.onClick,  this);
46092     },
46093
46094
46095     getResizeEl : function(){
46096         return this.wrap;
46097     },
46098
46099     getPositionEl : function(){
46100         return this.wrap;
46101     },
46102
46103     
46104     // private
46105     onRender : function(ct, position){
46106         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46107        
46108         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46109         
46110         var r1 = '<table><tr>';
46111         var r2 = '<tr class="x-form-daypick-icons">';
46112         for (var i=0; i < 7; i++) {
46113             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46114             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46115         }
46116         
46117         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46118         viewEl.select('img').on('click', this.onClick, this);
46119         this.viewEl = viewEl;   
46120         
46121         
46122         // this will not work on Chrome!!!
46123         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46124         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46125         
46126         
46127           
46128
46129     },
46130
46131     // private
46132     initValue : Roo.emptyFn,
46133
46134     /**
46135      * Returns the checked state of the checkbox.
46136      * @return {Boolean} True if checked, else false
46137      */
46138     getValue : function(){
46139         return this.el.dom.value;
46140         
46141     },
46142
46143         // private
46144     onClick : function(e){ 
46145         //this.setChecked(!this.checked);
46146         Roo.get(e.target).toggleClass('x-menu-item-checked');
46147         this.refreshValue();
46148         //if(this.el.dom.checked != this.checked){
46149         //    this.setValue(this.el.dom.checked);
46150        // }
46151     },
46152     
46153     // private
46154     refreshValue : function()
46155     {
46156         var val = '';
46157         this.viewEl.select('img',true).each(function(e,i,n)  {
46158             val += e.is(".x-menu-item-checked") ? String(n) : '';
46159         });
46160         this.setValue(val, true);
46161     },
46162
46163     /**
46164      * Sets the checked state of the checkbox.
46165      * On is always based on a string comparison between inputValue and the param.
46166      * @param {Boolean/String} value - the value to set 
46167      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46168      */
46169     setValue : function(v,suppressEvent){
46170         if (!this.el.dom) {
46171             return;
46172         }
46173         var old = this.el.dom.value ;
46174         this.el.dom.value = v;
46175         if (suppressEvent) {
46176             return ;
46177         }
46178          
46179         // update display..
46180         this.viewEl.select('img',true).each(function(e,i,n)  {
46181             
46182             var on = e.is(".x-menu-item-checked");
46183             var newv = v.indexOf(String(n)) > -1;
46184             if (on != newv) {
46185                 e.toggleClass('x-menu-item-checked');
46186             }
46187             
46188         });
46189         
46190         
46191         this.fireEvent('change', this, v, old);
46192         
46193         
46194     },
46195    
46196     // handle setting of hidden value by some other method!!?!?
46197     setFromHidden: function()
46198     {
46199         if(!this.el){
46200             return;
46201         }
46202         //console.log("SET FROM HIDDEN");
46203         //alert('setFrom hidden');
46204         this.setValue(this.el.dom.value);
46205     },
46206     
46207     onDestroy : function()
46208     {
46209         if(this.viewEl){
46210             Roo.get(this.viewEl).remove();
46211         }
46212          
46213         Roo.form.DayPicker.superclass.onDestroy.call(this);
46214     }
46215
46216 });/*
46217  * RooJS Library 1.1.1
46218  * Copyright(c) 2008-2011  Alan Knowles
46219  *
46220  * License - LGPL
46221  */
46222  
46223
46224 /**
46225  * @class Roo.form.ComboCheck
46226  * @extends Roo.form.ComboBox
46227  * A combobox for multiple select items.
46228  *
46229  * FIXME - could do with a reset button..
46230  * 
46231  * @constructor
46232  * Create a new ComboCheck
46233  * @param {Object} config Configuration options
46234  */
46235 Roo.form.ComboCheck = function(config){
46236     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46237     // should verify some data...
46238     // like
46239     // hiddenName = required..
46240     // displayField = required
46241     // valudField == required
46242     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46243     var _t = this;
46244     Roo.each(req, function(e) {
46245         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46246             throw "Roo.form.ComboCheck : missing value for: " + e;
46247         }
46248     });
46249     
46250     
46251 };
46252
46253 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46254      
46255      
46256     editable : false,
46257      
46258     selectedClass: 'x-menu-item-checked', 
46259     
46260     // private
46261     onRender : function(ct, position){
46262         var _t = this;
46263         
46264         
46265         
46266         if(!this.tpl){
46267             var cls = 'x-combo-list';
46268
46269             
46270             this.tpl =  new Roo.Template({
46271                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46272                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46273                    '<span>{' + this.displayField + '}</span>' +
46274                     '</div>' 
46275                 
46276             });
46277         }
46278  
46279         
46280         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46281         this.view.singleSelect = false;
46282         this.view.multiSelect = true;
46283         this.view.toggleSelect = true;
46284         this.pageTb.add(new Roo.Toolbar.Fill(), {
46285             
46286             text: 'Done',
46287             handler: function()
46288             {
46289                 _t.collapse();
46290             }
46291         });
46292     },
46293     
46294     onViewOver : function(e, t){
46295         // do nothing...
46296         return;
46297         
46298     },
46299     
46300     onViewClick : function(doFocus,index){
46301         return;
46302         
46303     },
46304     select: function () {
46305         //Roo.log("SELECT CALLED");
46306     },
46307      
46308     selectByValue : function(xv, scrollIntoView){
46309         var ar = this.getValueArray();
46310         var sels = [];
46311         
46312         Roo.each(ar, function(v) {
46313             if(v === undefined || v === null){
46314                 return;
46315             }
46316             var r = this.findRecord(this.valueField, v);
46317             if(r){
46318                 sels.push(this.store.indexOf(r))
46319                 
46320             }
46321         },this);
46322         this.view.select(sels);
46323         return false;
46324     },
46325     
46326     
46327     
46328     onSelect : function(record, index){
46329        // Roo.log("onselect Called");
46330        // this is only called by the clear button now..
46331         this.view.clearSelections();
46332         this.setValue('[]');
46333         if (this.value != this.valueBefore) {
46334             this.fireEvent('change', this, this.value, this.valueBefore);
46335             this.valueBefore = this.value;
46336         }
46337     },
46338     getValueArray : function()
46339     {
46340         var ar = [] ;
46341         
46342         try {
46343             //Roo.log(this.value);
46344             if (typeof(this.value) == 'undefined') {
46345                 return [];
46346             }
46347             var ar = Roo.decode(this.value);
46348             return  ar instanceof Array ? ar : []; //?? valid?
46349             
46350         } catch(e) {
46351             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46352             return [];
46353         }
46354          
46355     },
46356     expand : function ()
46357     {
46358         
46359         Roo.form.ComboCheck.superclass.expand.call(this);
46360         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46361         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46362         
46363
46364     },
46365     
46366     collapse : function(){
46367         Roo.form.ComboCheck.superclass.collapse.call(this);
46368         var sl = this.view.getSelectedIndexes();
46369         var st = this.store;
46370         var nv = [];
46371         var tv = [];
46372         var r;
46373         Roo.each(sl, function(i) {
46374             r = st.getAt(i);
46375             nv.push(r.get(this.valueField));
46376         },this);
46377         this.setValue(Roo.encode(nv));
46378         if (this.value != this.valueBefore) {
46379
46380             this.fireEvent('change', this, this.value, this.valueBefore);
46381             this.valueBefore = this.value;
46382         }
46383         
46384     },
46385     
46386     setValue : function(v){
46387         // Roo.log(v);
46388         this.value = v;
46389         
46390         var vals = this.getValueArray();
46391         var tv = [];
46392         Roo.each(vals, function(k) {
46393             var r = this.findRecord(this.valueField, k);
46394             if(r){
46395                 tv.push(r.data[this.displayField]);
46396             }else if(this.valueNotFoundText !== undefined){
46397                 tv.push( this.valueNotFoundText );
46398             }
46399         },this);
46400        // Roo.log(tv);
46401         
46402         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46403         this.hiddenField.value = v;
46404         this.value = v;
46405     }
46406     
46407 });/*
46408  * Based on:
46409  * Ext JS Library 1.1.1
46410  * Copyright(c) 2006-2007, Ext JS, LLC.
46411  *
46412  * Originally Released Under LGPL - original licence link has changed is not relivant.
46413  *
46414  * Fork - LGPL
46415  * <script type="text/javascript">
46416  */
46417  
46418 /**
46419  * @class Roo.form.Signature
46420  * @extends Roo.form.Field
46421  * Signature field.  
46422  * @constructor
46423  * 
46424  * @param {Object} config Configuration options
46425  */
46426
46427 Roo.form.Signature = function(config){
46428     Roo.form.Signature.superclass.constructor.call(this, config);
46429     
46430     this.addEvents({// not in used??
46431          /**
46432          * @event confirm
46433          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46434              * @param {Roo.form.Signature} combo This combo box
46435              */
46436         'confirm' : true,
46437         /**
46438          * @event reset
46439          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46440              * @param {Roo.form.ComboBox} combo This combo box
46441              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46442              */
46443         'reset' : true
46444     });
46445 };
46446
46447 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46448     /**
46449      * @cfg {Object} labels Label to use when rendering a form.
46450      * defaults to 
46451      * labels : { 
46452      *      clear : "Clear",
46453      *      confirm : "Confirm"
46454      *  }
46455      */
46456     labels : { 
46457         clear : "Clear",
46458         confirm : "Confirm"
46459     },
46460     /**
46461      * @cfg {Number} width The signature panel width (defaults to 300)
46462      */
46463     width: 300,
46464     /**
46465      * @cfg {Number} height The signature panel height (defaults to 100)
46466      */
46467     height : 100,
46468     /**
46469      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46470      */
46471     allowBlank : false,
46472     
46473     //private
46474     // {Object} signPanel The signature SVG panel element (defaults to {})
46475     signPanel : {},
46476     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46477     isMouseDown : false,
46478     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46479     isConfirmed : false,
46480     // {String} signatureTmp SVG mapping string (defaults to empty string)
46481     signatureTmp : '',
46482     
46483     
46484     defaultAutoCreate : { // modified by initCompnoent..
46485         tag: "input",
46486         type:"hidden"
46487     },
46488
46489     // private
46490     onRender : function(ct, position){
46491         
46492         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46493         
46494         this.wrap = this.el.wrap({
46495             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46496         });
46497         
46498         this.createToolbar(this);
46499         this.signPanel = this.wrap.createChild({
46500                 tag: 'div',
46501                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46502             }, this.el
46503         );
46504             
46505         this.svgID = Roo.id();
46506         this.svgEl = this.signPanel.createChild({
46507               xmlns : 'http://www.w3.org/2000/svg',
46508               tag : 'svg',
46509               id : this.svgID + "-svg",
46510               width: this.width,
46511               height: this.height,
46512               viewBox: '0 0 '+this.width+' '+this.height,
46513               cn : [
46514                 {
46515                     tag: "rect",
46516                     id: this.svgID + "-svg-r",
46517                     width: this.width,
46518                     height: this.height,
46519                     fill: "#ffa"
46520                 },
46521                 {
46522                     tag: "line",
46523                     id: this.svgID + "-svg-l",
46524                     x1: "0", // start
46525                     y1: (this.height*0.8), // start set the line in 80% of height
46526                     x2: this.width, // end
46527                     y2: (this.height*0.8), // end set the line in 80% of height
46528                     'stroke': "#666",
46529                     'stroke-width': "1",
46530                     'stroke-dasharray': "3",
46531                     'shape-rendering': "crispEdges",
46532                     'pointer-events': "none"
46533                 },
46534                 {
46535                     tag: "path",
46536                     id: this.svgID + "-svg-p",
46537                     'stroke': "navy",
46538                     'stroke-width': "3",
46539                     'fill': "none",
46540                     'pointer-events': 'none'
46541                 }
46542               ]
46543         });
46544         this.createSVG();
46545         this.svgBox = this.svgEl.dom.getScreenCTM();
46546     },
46547     createSVG : function(){ 
46548         var svg = this.signPanel;
46549         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46550         var t = this;
46551
46552         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46553         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46554         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46555         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46556         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46557         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46558         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46559         
46560     },
46561     isTouchEvent : function(e){
46562         return e.type.match(/^touch/);
46563     },
46564     getCoords : function (e) {
46565         var pt    = this.svgEl.dom.createSVGPoint();
46566         pt.x = e.clientX; 
46567         pt.y = e.clientY;
46568         if (this.isTouchEvent(e)) {
46569             pt.x =  e.targetTouches[0].clientX 
46570             pt.y = e.targetTouches[0].clientY;
46571         }
46572         var a = this.svgEl.dom.getScreenCTM();
46573         var b = a.inverse();
46574         var mx = pt.matrixTransform(b);
46575         return mx.x + ',' + mx.y;
46576     },
46577     //mouse event headler 
46578     down : function (e) {
46579         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46580         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46581         
46582         this.isMouseDown = true;
46583         
46584         e.preventDefault();
46585     },
46586     move : function (e) {
46587         if (this.isMouseDown) {
46588             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46589             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46590         }
46591         
46592         e.preventDefault();
46593     },
46594     up : function (e) {
46595         this.isMouseDown = false;
46596         var sp = this.signatureTmp.split(' ');
46597         
46598         if(sp.length > 1){
46599             if(!sp[sp.length-2].match(/^L/)){
46600                 sp.pop();
46601                 sp.pop();
46602                 sp.push("");
46603                 this.signatureTmp = sp.join(" ");
46604             }
46605         }
46606         if(this.getValue() != this.signatureTmp){
46607             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46608             this.isConfirmed = false;
46609         }
46610         e.preventDefault();
46611     },
46612     
46613     /**
46614      * Protected method that will not generally be called directly. It
46615      * is called when the editor creates its toolbar. Override this method if you need to
46616      * add custom toolbar buttons.
46617      * @param {HtmlEditor} editor
46618      */
46619     createToolbar : function(editor){
46620          function btn(id, toggle, handler){
46621             var xid = fid + '-'+ id ;
46622             return {
46623                 id : xid,
46624                 cmd : id,
46625                 cls : 'x-btn-icon x-edit-'+id,
46626                 enableToggle:toggle !== false,
46627                 scope: editor, // was editor...
46628                 handler:handler||editor.relayBtnCmd,
46629                 clickEvent:'mousedown',
46630                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46631                 tabIndex:-1
46632             };
46633         }
46634         
46635         
46636         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46637         this.tb = tb;
46638         this.tb.add(
46639            {
46640                 cls : ' x-signature-btn x-signature-'+id,
46641                 scope: editor, // was editor...
46642                 handler: this.reset,
46643                 clickEvent:'mousedown',
46644                 text: this.labels.clear
46645             },
46646             {
46647                  xtype : 'Fill',
46648                  xns: Roo.Toolbar
46649             }, 
46650             {
46651                 cls : '  x-signature-btn x-signature-'+id,
46652                 scope: editor, // was editor...
46653                 handler: this.confirmHandler,
46654                 clickEvent:'mousedown',
46655                 text: this.labels.confirm
46656             }
46657         );
46658     
46659     },
46660     //public
46661     /**
46662      * when user is clicked confirm then show this image.....
46663      * 
46664      * @return {String} Image Data URI
46665      */
46666     getImageDataURI : function(){
46667         var svg = this.svgEl.dom.parentNode.innerHTML;
46668         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46669         return src; 
46670     },
46671     /**
46672      * 
46673      * @return {Boolean} this.isConfirmed
46674      */
46675     getConfirmed : function(){
46676         return this.isConfirmed;
46677     },
46678     /**
46679      * 
46680      * @return {Number} this.width
46681      */
46682     getWidth : function(){
46683         return this.width;
46684     },
46685     /**
46686      * 
46687      * @return {Number} this.height
46688      */
46689     getHeight : function(){
46690         return this.height;
46691     },
46692     // private
46693     getSignature : function(){
46694         return this.signatureTmp;
46695     },
46696     // private
46697     reset : function(){
46698         this.signatureTmp = '';
46699         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46700         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46701         this.isConfirmed = false;
46702         Roo.form.Signature.superclass.reset.call(this);
46703     },
46704     setSignature : function(s){
46705         this.signatureTmp = s;
46706         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46707         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46708         this.setValue(s);
46709         this.isConfirmed = false;
46710         Roo.form.Signature.superclass.reset.call(this);
46711     }, 
46712     test : function(){
46713 //        Roo.log(this.signPanel.dom.contentWindow.up())
46714     },
46715     //private
46716     setConfirmed : function(){
46717         
46718         
46719         
46720 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46721     },
46722     // private
46723     confirmHandler : function(){
46724         if(!this.getSignature()){
46725             return;
46726         }
46727         
46728         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46729         this.setValue(this.getSignature());
46730         this.isConfirmed = true;
46731         
46732         this.fireEvent('confirm', this);
46733     },
46734     // private
46735     // Subclasses should provide the validation implementation by overriding this
46736     validateValue : function(value){
46737         if(this.allowBlank){
46738             return true;
46739         }
46740         
46741         if(this.isConfirmed){
46742             return true;
46743         }
46744         return false;
46745     }
46746 });/*
46747  * Based on:
46748  * Ext JS Library 1.1.1
46749  * Copyright(c) 2006-2007, Ext JS, LLC.
46750  *
46751  * Originally Released Under LGPL - original licence link has changed is not relivant.
46752  *
46753  * Fork - LGPL
46754  * <script type="text/javascript">
46755  */
46756  
46757
46758 /**
46759  * @class Roo.form.ComboBox
46760  * @extends Roo.form.TriggerField
46761  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46762  * @constructor
46763  * Create a new ComboBox.
46764  * @param {Object} config Configuration options
46765  */
46766 Roo.form.Select = function(config){
46767     Roo.form.Select.superclass.constructor.call(this, config);
46768      
46769 };
46770
46771 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46772     /**
46773      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46774      */
46775     /**
46776      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46777      * rendering into an Roo.Editor, defaults to false)
46778      */
46779     /**
46780      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46781      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46782      */
46783     /**
46784      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46785      */
46786     /**
46787      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46788      * the dropdown list (defaults to undefined, with no header element)
46789      */
46790
46791      /**
46792      * @cfg {String/Roo.Template} tpl The template to use to render the output
46793      */
46794      
46795     // private
46796     defaultAutoCreate : {tag: "select"  },
46797     /**
46798      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46799      */
46800     listWidth: undefined,
46801     /**
46802      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46803      * mode = 'remote' or 'text' if mode = 'local')
46804      */
46805     displayField: undefined,
46806     /**
46807      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46808      * mode = 'remote' or 'value' if mode = 'local'). 
46809      * Note: use of a valueField requires the user make a selection
46810      * in order for a value to be mapped.
46811      */
46812     valueField: undefined,
46813     
46814     
46815     /**
46816      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46817      * field's data value (defaults to the underlying DOM element's name)
46818      */
46819     hiddenName: undefined,
46820     /**
46821      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46822      */
46823     listClass: '',
46824     /**
46825      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46826      */
46827     selectedClass: 'x-combo-selected',
46828     /**
46829      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46830      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46831      * which displays a downward arrow icon).
46832      */
46833     triggerClass : 'x-form-arrow-trigger',
46834     /**
46835      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46836      */
46837     shadow:'sides',
46838     /**
46839      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46840      * anchor positions (defaults to 'tl-bl')
46841      */
46842     listAlign: 'tl-bl?',
46843     /**
46844      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46845      */
46846     maxHeight: 300,
46847     /**
46848      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46849      * query specified by the allQuery config option (defaults to 'query')
46850      */
46851     triggerAction: 'query',
46852     /**
46853      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46854      * (defaults to 4, does not apply if editable = false)
46855      */
46856     minChars : 4,
46857     /**
46858      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46859      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46860      */
46861     typeAhead: false,
46862     /**
46863      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46864      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46865      */
46866     queryDelay: 500,
46867     /**
46868      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46869      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46870      */
46871     pageSize: 0,
46872     /**
46873      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46874      * when editable = true (defaults to false)
46875      */
46876     selectOnFocus:false,
46877     /**
46878      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46879      */
46880     queryParam: 'query',
46881     /**
46882      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46883      * when mode = 'remote' (defaults to 'Loading...')
46884      */
46885     loadingText: 'Loading...',
46886     /**
46887      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46888      */
46889     resizable: false,
46890     /**
46891      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46892      */
46893     handleHeight : 8,
46894     /**
46895      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46896      * traditional select (defaults to true)
46897      */
46898     editable: true,
46899     /**
46900      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46901      */
46902     allQuery: '',
46903     /**
46904      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46905      */
46906     mode: 'remote',
46907     /**
46908      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46909      * listWidth has a higher value)
46910      */
46911     minListWidth : 70,
46912     /**
46913      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46914      * allow the user to set arbitrary text into the field (defaults to false)
46915      */
46916     forceSelection:false,
46917     /**
46918      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46919      * if typeAhead = true (defaults to 250)
46920      */
46921     typeAheadDelay : 250,
46922     /**
46923      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46924      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46925      */
46926     valueNotFoundText : undefined,
46927     
46928     /**
46929      * @cfg {String} defaultValue The value displayed after loading the store.
46930      */
46931     defaultValue: '',
46932     
46933     /**
46934      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46935      */
46936     blockFocus : false,
46937     
46938     /**
46939      * @cfg {Boolean} disableClear Disable showing of clear button.
46940      */
46941     disableClear : false,
46942     /**
46943      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46944      */
46945     alwaysQuery : false,
46946     
46947     //private
46948     addicon : false,
46949     editicon: false,
46950     
46951     // element that contains real text value.. (when hidden is used..)
46952      
46953     // private
46954     onRender : function(ct, position){
46955         Roo.form.Field.prototype.onRender.call(this, ct, position);
46956         
46957         if(this.store){
46958             this.store.on('beforeload', this.onBeforeLoad, this);
46959             this.store.on('load', this.onLoad, this);
46960             this.store.on('loadexception', this.onLoadException, this);
46961             this.store.load({});
46962         }
46963         
46964         
46965         
46966     },
46967
46968     // private
46969     initEvents : function(){
46970         //Roo.form.ComboBox.superclass.initEvents.call(this);
46971  
46972     },
46973
46974     onDestroy : function(){
46975        
46976         if(this.store){
46977             this.store.un('beforeload', this.onBeforeLoad, this);
46978             this.store.un('load', this.onLoad, this);
46979             this.store.un('loadexception', this.onLoadException, this);
46980         }
46981         //Roo.form.ComboBox.superclass.onDestroy.call(this);
46982     },
46983
46984     // private
46985     fireKey : function(e){
46986         if(e.isNavKeyPress() && !this.list.isVisible()){
46987             this.fireEvent("specialkey", this, e);
46988         }
46989     },
46990
46991     // private
46992     onResize: function(w, h){
46993         
46994         return; 
46995     
46996         
46997     },
46998
46999     /**
47000      * Allow or prevent the user from directly editing the field text.  If false is passed,
47001      * the user will only be able to select from the items defined in the dropdown list.  This method
47002      * is the runtime equivalent of setting the 'editable' config option at config time.
47003      * @param {Boolean} value True to allow the user to directly edit the field text
47004      */
47005     setEditable : function(value){
47006          
47007     },
47008
47009     // private
47010     onBeforeLoad : function(){
47011         
47012         Roo.log("Select before load");
47013         return;
47014     
47015         this.innerList.update(this.loadingText ?
47016                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47017         //this.restrictHeight();
47018         this.selectedIndex = -1;
47019     },
47020
47021     // private
47022     onLoad : function(){
47023
47024     
47025         var dom = this.el.dom;
47026         dom.innerHTML = '';
47027          var od = dom.ownerDocument;
47028          
47029         if (this.emptyText) {
47030             var op = od.createElement('option');
47031             op.setAttribute('value', '');
47032             op.innerHTML = String.format('{0}', this.emptyText);
47033             dom.appendChild(op);
47034         }
47035         if(this.store.getCount() > 0){
47036            
47037             var vf = this.valueField;
47038             var df = this.displayField;
47039             this.store.data.each(function(r) {
47040                 // which colmsn to use... testing - cdoe / title..
47041                 var op = od.createElement('option');
47042                 op.setAttribute('value', r.data[vf]);
47043                 op.innerHTML = String.format('{0}', r.data[df]);
47044                 dom.appendChild(op);
47045             });
47046             if (typeof(this.defaultValue != 'undefined')) {
47047                 this.setValue(this.defaultValue);
47048             }
47049             
47050              
47051         }else{
47052             //this.onEmptyResults();
47053         }
47054         //this.el.focus();
47055     },
47056     // private
47057     onLoadException : function()
47058     {
47059         dom.innerHTML = '';
47060             
47061         Roo.log("Select on load exception");
47062         return;
47063     
47064         this.collapse();
47065         Roo.log(this.store.reader.jsonData);
47066         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47067             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47068         }
47069         
47070         
47071     },
47072     // private
47073     onTypeAhead : function(){
47074          
47075     },
47076
47077     // private
47078     onSelect : function(record, index){
47079         Roo.log('on select?');
47080         return;
47081         if(this.fireEvent('beforeselect', this, record, index) !== false){
47082             this.setFromData(index > -1 ? record.data : false);
47083             this.collapse();
47084             this.fireEvent('select', this, record, index);
47085         }
47086     },
47087
47088     /**
47089      * Returns the currently selected field value or empty string if no value is set.
47090      * @return {String} value The selected value
47091      */
47092     getValue : function(){
47093         var dom = this.el.dom;
47094         this.value = dom.options[dom.selectedIndex].value;
47095         return this.value;
47096         
47097     },
47098
47099     /**
47100      * Clears any text/value currently set in the field
47101      */
47102     clearValue : function(){
47103         this.value = '';
47104         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47105         
47106     },
47107
47108     /**
47109      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47110      * will be displayed in the field.  If the value does not match the data value of an existing item,
47111      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47112      * Otherwise the field will be blank (although the value will still be set).
47113      * @param {String} value The value to match
47114      */
47115     setValue : function(v){
47116         var d = this.el.dom;
47117         for (var i =0; i < d.options.length;i++) {
47118             if (v == d.options[i].value) {
47119                 d.selectedIndex = i;
47120                 this.value = v;
47121                 return;
47122             }
47123         }
47124         this.clearValue();
47125     },
47126     /**
47127      * @property {Object} the last set data for the element
47128      */
47129     
47130     lastData : false,
47131     /**
47132      * Sets the value of the field based on a object which is related to the record format for the store.
47133      * @param {Object} value the value to set as. or false on reset?
47134      */
47135     setFromData : function(o){
47136         Roo.log('setfrom data?');
47137          
47138         
47139         
47140     },
47141     // private
47142     reset : function(){
47143         this.clearValue();
47144     },
47145     // private
47146     findRecord : function(prop, value){
47147         
47148         return false;
47149     
47150         var record;
47151         if(this.store.getCount() > 0){
47152             this.store.each(function(r){
47153                 if(r.data[prop] == value){
47154                     record = r;
47155                     return false;
47156                 }
47157                 return true;
47158             });
47159         }
47160         return record;
47161     },
47162     
47163     getName: function()
47164     {
47165         // returns hidden if it's set..
47166         if (!this.rendered) {return ''};
47167         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47168         
47169     },
47170      
47171
47172     
47173
47174     // private
47175     onEmptyResults : function(){
47176         Roo.log('empty results');
47177         //this.collapse();
47178     },
47179
47180     /**
47181      * Returns true if the dropdown list is expanded, else false.
47182      */
47183     isExpanded : function(){
47184         return false;
47185     },
47186
47187     /**
47188      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47189      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47190      * @param {String} value The data value of the item to select
47191      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47192      * selected item if it is not currently in view (defaults to true)
47193      * @return {Boolean} True if the value matched an item in the list, else false
47194      */
47195     selectByValue : function(v, scrollIntoView){
47196         Roo.log('select By Value');
47197         return false;
47198     
47199         if(v !== undefined && v !== null){
47200             var r = this.findRecord(this.valueField || this.displayField, v);
47201             if(r){
47202                 this.select(this.store.indexOf(r), scrollIntoView);
47203                 return true;
47204             }
47205         }
47206         return false;
47207     },
47208
47209     /**
47210      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47211      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47212      * @param {Number} index The zero-based index of the list item to select
47213      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47214      * selected item if it is not currently in view (defaults to true)
47215      */
47216     select : function(index, scrollIntoView){
47217         Roo.log('select ');
47218         return  ;
47219         
47220         this.selectedIndex = index;
47221         this.view.select(index);
47222         if(scrollIntoView !== false){
47223             var el = this.view.getNode(index);
47224             if(el){
47225                 this.innerList.scrollChildIntoView(el, false);
47226             }
47227         }
47228     },
47229
47230       
47231
47232     // private
47233     validateBlur : function(){
47234         
47235         return;
47236         
47237     },
47238
47239     // private
47240     initQuery : function(){
47241         this.doQuery(this.getRawValue());
47242     },
47243
47244     // private
47245     doForce : function(){
47246         if(this.el.dom.value.length > 0){
47247             this.el.dom.value =
47248                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47249              
47250         }
47251     },
47252
47253     /**
47254      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47255      * query allowing the query action to be canceled if needed.
47256      * @param {String} query The SQL query to execute
47257      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47258      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47259      * saved in the current store (defaults to false)
47260      */
47261     doQuery : function(q, forceAll){
47262         
47263         Roo.log('doQuery?');
47264         if(q === undefined || q === null){
47265             q = '';
47266         }
47267         var qe = {
47268             query: q,
47269             forceAll: forceAll,
47270             combo: this,
47271             cancel:false
47272         };
47273         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47274             return false;
47275         }
47276         q = qe.query;
47277         forceAll = qe.forceAll;
47278         if(forceAll === true || (q.length >= this.minChars)){
47279             if(this.lastQuery != q || this.alwaysQuery){
47280                 this.lastQuery = q;
47281                 if(this.mode == 'local'){
47282                     this.selectedIndex = -1;
47283                     if(forceAll){
47284                         this.store.clearFilter();
47285                     }else{
47286                         this.store.filter(this.displayField, q);
47287                     }
47288                     this.onLoad();
47289                 }else{
47290                     this.store.baseParams[this.queryParam] = q;
47291                     this.store.load({
47292                         params: this.getParams(q)
47293                     });
47294                     this.expand();
47295                 }
47296             }else{
47297                 this.selectedIndex = -1;
47298                 this.onLoad();   
47299             }
47300         }
47301     },
47302
47303     // private
47304     getParams : function(q){
47305         var p = {};
47306         //p[this.queryParam] = q;
47307         if(this.pageSize){
47308             p.start = 0;
47309             p.limit = this.pageSize;
47310         }
47311         return p;
47312     },
47313
47314     /**
47315      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47316      */
47317     collapse : function(){
47318         
47319     },
47320
47321     // private
47322     collapseIf : function(e){
47323         
47324     },
47325
47326     /**
47327      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47328      */
47329     expand : function(){
47330         
47331     } ,
47332
47333     // private
47334      
47335
47336     /** 
47337     * @cfg {Boolean} grow 
47338     * @hide 
47339     */
47340     /** 
47341     * @cfg {Number} growMin 
47342     * @hide 
47343     */
47344     /** 
47345     * @cfg {Number} growMax 
47346     * @hide 
47347     */
47348     /**
47349      * @hide
47350      * @method autoSize
47351      */
47352     
47353     setWidth : function()
47354     {
47355         
47356     },
47357     getResizeEl : function(){
47358         return this.el;
47359     }
47360 });//<script type="text/javasscript">
47361  
47362
47363 /**
47364  * @class Roo.DDView
47365  * A DnD enabled version of Roo.View.
47366  * @param {Element/String} container The Element in which to create the View.
47367  * @param {String} tpl The template string used to create the markup for each element of the View
47368  * @param {Object} config The configuration properties. These include all the config options of
47369  * {@link Roo.View} plus some specific to this class.<br>
47370  * <p>
47371  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47372  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47373  * <p>
47374  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47375 .x-view-drag-insert-above {
47376         border-top:1px dotted #3366cc;
47377 }
47378 .x-view-drag-insert-below {
47379         border-bottom:1px dotted #3366cc;
47380 }
47381 </code></pre>
47382  * 
47383  */
47384  
47385 Roo.DDView = function(container, tpl, config) {
47386     Roo.DDView.superclass.constructor.apply(this, arguments);
47387     this.getEl().setStyle("outline", "0px none");
47388     this.getEl().unselectable();
47389     if (this.dragGroup) {
47390                 this.setDraggable(this.dragGroup.split(","));
47391     }
47392     if (this.dropGroup) {
47393                 this.setDroppable(this.dropGroup.split(","));
47394     }
47395     if (this.deletable) {
47396         this.setDeletable();
47397     }
47398     this.isDirtyFlag = false;
47399         this.addEvents({
47400                 "drop" : true
47401         });
47402 };
47403
47404 Roo.extend(Roo.DDView, Roo.View, {
47405 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47406 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47407 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47408 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47409
47410         isFormField: true,
47411
47412         reset: Roo.emptyFn,
47413         
47414         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47415
47416         validate: function() {
47417                 return true;
47418         },
47419         
47420         destroy: function() {
47421                 this.purgeListeners();
47422                 this.getEl.removeAllListeners();
47423                 this.getEl().remove();
47424                 if (this.dragZone) {
47425                         if (this.dragZone.destroy) {
47426                                 this.dragZone.destroy();
47427                         }
47428                 }
47429                 if (this.dropZone) {
47430                         if (this.dropZone.destroy) {
47431                                 this.dropZone.destroy();
47432                         }
47433                 }
47434         },
47435
47436 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47437         getName: function() {
47438                 return this.name;
47439         },
47440
47441 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47442         setValue: function(v) {
47443                 if (!this.store) {
47444                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47445                 }
47446                 var data = {};
47447                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47448                 this.store.proxy = new Roo.data.MemoryProxy(data);
47449                 this.store.load();
47450         },
47451
47452 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47453         getValue: function() {
47454                 var result = '(';
47455                 this.store.each(function(rec) {
47456                         result += rec.id + ',';
47457                 });
47458                 return result.substr(0, result.length - 1) + ')';
47459         },
47460         
47461         getIds: function() {
47462                 var i = 0, result = new Array(this.store.getCount());
47463                 this.store.each(function(rec) {
47464                         result[i++] = rec.id;
47465                 });
47466                 return result;
47467         },
47468         
47469         isDirty: function() {
47470                 return this.isDirtyFlag;
47471         },
47472
47473 /**
47474  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47475  *      whole Element becomes the target, and this causes the drop gesture to append.
47476  */
47477     getTargetFromEvent : function(e) {
47478                 var target = e.getTarget();
47479                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47480                 target = target.parentNode;
47481                 }
47482                 if (!target) {
47483                         target = this.el.dom.lastChild || this.el.dom;
47484                 }
47485                 return target;
47486     },
47487
47488 /**
47489  *      Create the drag data which consists of an object which has the property "ddel" as
47490  *      the drag proxy element. 
47491  */
47492     getDragData : function(e) {
47493         var target = this.findItemFromChild(e.getTarget());
47494                 if(target) {
47495                         this.handleSelection(e);
47496                         var selNodes = this.getSelectedNodes();
47497             var dragData = {
47498                 source: this,
47499                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47500                 nodes: selNodes,
47501                 records: []
47502                         };
47503                         var selectedIndices = this.getSelectedIndexes();
47504                         for (var i = 0; i < selectedIndices.length; i++) {
47505                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47506                         }
47507                         if (selNodes.length == 1) {
47508                                 dragData.ddel = target.cloneNode(true); // the div element
47509                         } else {
47510                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47511                                 div.className = 'multi-proxy';
47512                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47513                                         div.appendChild(selNodes[i].cloneNode(true));
47514                                 }
47515                                 dragData.ddel = div;
47516                         }
47517             //console.log(dragData)
47518             //console.log(dragData.ddel.innerHTML)
47519                         return dragData;
47520                 }
47521         //console.log('nodragData')
47522                 return false;
47523     },
47524     
47525 /**     Specify to which ddGroup items in this DDView may be dragged. */
47526     setDraggable: function(ddGroup) {
47527         if (ddGroup instanceof Array) {
47528                 Roo.each(ddGroup, this.setDraggable, this);
47529                 return;
47530         }
47531         if (this.dragZone) {
47532                 this.dragZone.addToGroup(ddGroup);
47533         } else {
47534                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47535                                 containerScroll: true,
47536                                 ddGroup: ddGroup 
47537
47538                         });
47539 //                      Draggability implies selection. DragZone's mousedown selects the element.
47540                         if (!this.multiSelect) { this.singleSelect = true; }
47541
47542 //                      Wire the DragZone's handlers up to methods in *this*
47543                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47544                 }
47545     },
47546
47547 /**     Specify from which ddGroup this DDView accepts drops. */
47548     setDroppable: function(ddGroup) {
47549         if (ddGroup instanceof Array) {
47550                 Roo.each(ddGroup, this.setDroppable, this);
47551                 return;
47552         }
47553         if (this.dropZone) {
47554                 this.dropZone.addToGroup(ddGroup);
47555         } else {
47556                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47557                                 containerScroll: true,
47558                                 ddGroup: ddGroup
47559                         });
47560
47561 //                      Wire the DropZone's handlers up to methods in *this*
47562                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47563                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47564                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47565                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47566                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47567                 }
47568     },
47569
47570 /**     Decide whether to drop above or below a View node. */
47571     getDropPoint : function(e, n, dd){
47572         if (n == this.el.dom) { return "above"; }
47573                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47574                 var c = t + (b - t) / 2;
47575                 var y = Roo.lib.Event.getPageY(e);
47576                 if(y <= c) {
47577                         return "above";
47578                 }else{
47579                         return "below";
47580                 }
47581     },
47582
47583     onNodeEnter : function(n, dd, e, data){
47584                 return false;
47585     },
47586     
47587     onNodeOver : function(n, dd, e, data){
47588                 var pt = this.getDropPoint(e, n, dd);
47589                 // set the insert point style on the target node
47590                 var dragElClass = this.dropNotAllowed;
47591                 if (pt) {
47592                         var targetElClass;
47593                         if (pt == "above"){
47594                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47595                                 targetElClass = "x-view-drag-insert-above";
47596                         } else {
47597                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47598                                 targetElClass = "x-view-drag-insert-below";
47599                         }
47600                         if (this.lastInsertClass != targetElClass){
47601                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47602                                 this.lastInsertClass = targetElClass;
47603                         }
47604                 }
47605                 return dragElClass;
47606         },
47607
47608     onNodeOut : function(n, dd, e, data){
47609                 this.removeDropIndicators(n);
47610     },
47611
47612     onNodeDrop : function(n, dd, e, data){
47613         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47614                 return false;
47615         }
47616         var pt = this.getDropPoint(e, n, dd);
47617                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47618                 if (pt == "below") { insertAt++; }
47619                 for (var i = 0; i < data.records.length; i++) {
47620                         var r = data.records[i];
47621                         var dup = this.store.getById(r.id);
47622                         if (dup && (dd != this.dragZone)) {
47623                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47624                         } else {
47625                                 if (data.copy) {
47626                                         this.store.insert(insertAt++, r.copy());
47627                                 } else {
47628                                         data.source.isDirtyFlag = true;
47629                                         r.store.remove(r);
47630                                         this.store.insert(insertAt++, r);
47631                                 }
47632                                 this.isDirtyFlag = true;
47633                         }
47634                 }
47635                 this.dragZone.cachedTarget = null;
47636                 return true;
47637     },
47638
47639     removeDropIndicators : function(n){
47640                 if(n){
47641                         Roo.fly(n).removeClass([
47642                                 "x-view-drag-insert-above",
47643                                 "x-view-drag-insert-below"]);
47644                         this.lastInsertClass = "_noclass";
47645                 }
47646     },
47647
47648 /**
47649  *      Utility method. Add a delete option to the DDView's context menu.
47650  *      @param {String} imageUrl The URL of the "delete" icon image.
47651  */
47652         setDeletable: function(imageUrl) {
47653                 if (!this.singleSelect && !this.multiSelect) {
47654                         this.singleSelect = true;
47655                 }
47656                 var c = this.getContextMenu();
47657                 this.contextMenu.on("itemclick", function(item) {
47658                         switch (item.id) {
47659                                 case "delete":
47660                                         this.remove(this.getSelectedIndexes());
47661                                         break;
47662                         }
47663                 }, this);
47664                 this.contextMenu.add({
47665                         icon: imageUrl,
47666                         id: "delete",
47667                         text: 'Delete'
47668                 });
47669         },
47670         
47671 /**     Return the context menu for this DDView. */
47672         getContextMenu: function() {
47673                 if (!this.contextMenu) {
47674 //                      Create the View's context menu
47675                         this.contextMenu = new Roo.menu.Menu({
47676                                 id: this.id + "-contextmenu"
47677                         });
47678                         this.el.on("contextmenu", this.showContextMenu, this);
47679                 }
47680                 return this.contextMenu;
47681         },
47682         
47683         disableContextMenu: function() {
47684                 if (this.contextMenu) {
47685                         this.el.un("contextmenu", this.showContextMenu, this);
47686                 }
47687         },
47688
47689         showContextMenu: function(e, item) {
47690         item = this.findItemFromChild(e.getTarget());
47691                 if (item) {
47692                         e.stopEvent();
47693                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47694                         this.contextMenu.showAt(e.getXY());
47695             }
47696     },
47697
47698 /**
47699  *      Remove {@link Roo.data.Record}s at the specified indices.
47700  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47701  */
47702     remove: function(selectedIndices) {
47703                 selectedIndices = [].concat(selectedIndices);
47704                 for (var i = 0; i < selectedIndices.length; i++) {
47705                         var rec = this.store.getAt(selectedIndices[i]);
47706                         this.store.remove(rec);
47707                 }
47708     },
47709
47710 /**
47711  *      Double click fires the event, but also, if this is draggable, and there is only one other
47712  *      related DropZone, it transfers the selected node.
47713  */
47714     onDblClick : function(e){
47715         var item = this.findItemFromChild(e.getTarget());
47716         if(item){
47717             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47718                 return false;
47719             }
47720             if (this.dragGroup) {
47721                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47722                     while (targets.indexOf(this.dropZone) > -1) {
47723                             targets.remove(this.dropZone);
47724                                 }
47725                     if (targets.length == 1) {
47726                                         this.dragZone.cachedTarget = null;
47727                         var el = Roo.get(targets[0].getEl());
47728                         var box = el.getBox(true);
47729                         targets[0].onNodeDrop(el.dom, {
47730                                 target: el.dom,
47731                                 xy: [box.x, box.y + box.height - 1]
47732                         }, null, this.getDragData(e));
47733                     }
47734                 }
47735         }
47736     },
47737     
47738     handleSelection: function(e) {
47739                 this.dragZone.cachedTarget = null;
47740         var item = this.findItemFromChild(e.getTarget());
47741         if (!item) {
47742                 this.clearSelections(true);
47743                 return;
47744         }
47745                 if (item && (this.multiSelect || this.singleSelect)){
47746                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47747                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47748                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47749                                 this.unselect(item);
47750                         } else {
47751                                 this.select(item, this.multiSelect && e.ctrlKey);
47752                                 this.lastSelection = item;
47753                         }
47754                 }
47755     },
47756
47757     onItemClick : function(item, index, e){
47758                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47759                         return false;
47760                 }
47761                 return true;
47762     },
47763
47764     unselect : function(nodeInfo, suppressEvent){
47765                 var node = this.getNode(nodeInfo);
47766                 if(node && this.isSelected(node)){
47767                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47768                                 Roo.fly(node).removeClass(this.selectedClass);
47769                                 this.selections.remove(node);
47770                                 if(!suppressEvent){
47771                                         this.fireEvent("selectionchange", this, this.selections);
47772                                 }
47773                         }
47774                 }
47775     }
47776 });
47777 /*
47778  * Based on:
47779  * Ext JS Library 1.1.1
47780  * Copyright(c) 2006-2007, Ext JS, LLC.
47781  *
47782  * Originally Released Under LGPL - original licence link has changed is not relivant.
47783  *
47784  * Fork - LGPL
47785  * <script type="text/javascript">
47786  */
47787  
47788 /**
47789  * @class Roo.LayoutManager
47790  * @extends Roo.util.Observable
47791  * Base class for layout managers.
47792  */
47793 Roo.LayoutManager = function(container, config){
47794     Roo.LayoutManager.superclass.constructor.call(this);
47795     this.el = Roo.get(container);
47796     // ie scrollbar fix
47797     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47798         document.body.scroll = "no";
47799     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47800         this.el.position('relative');
47801     }
47802     this.id = this.el.id;
47803     this.el.addClass("x-layout-container");
47804     /** false to disable window resize monitoring @type Boolean */
47805     this.monitorWindowResize = true;
47806     this.regions = {};
47807     this.addEvents({
47808         /**
47809          * @event layout
47810          * Fires when a layout is performed. 
47811          * @param {Roo.LayoutManager} this
47812          */
47813         "layout" : true,
47814         /**
47815          * @event regionresized
47816          * Fires when the user resizes a region. 
47817          * @param {Roo.LayoutRegion} region The resized region
47818          * @param {Number} newSize The new size (width for east/west, height for north/south)
47819          */
47820         "regionresized" : true,
47821         /**
47822          * @event regioncollapsed
47823          * Fires when a region is collapsed. 
47824          * @param {Roo.LayoutRegion} region The collapsed region
47825          */
47826         "regioncollapsed" : true,
47827         /**
47828          * @event regionexpanded
47829          * Fires when a region is expanded.  
47830          * @param {Roo.LayoutRegion} region The expanded region
47831          */
47832         "regionexpanded" : true
47833     });
47834     this.updating = false;
47835     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47836 };
47837
47838 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47839     /**
47840      * Returns true if this layout is currently being updated
47841      * @return {Boolean}
47842      */
47843     isUpdating : function(){
47844         return this.updating; 
47845     },
47846     
47847     /**
47848      * Suspend the LayoutManager from doing auto-layouts while
47849      * making multiple add or remove calls
47850      */
47851     beginUpdate : function(){
47852         this.updating = true;    
47853     },
47854     
47855     /**
47856      * Restore auto-layouts and optionally disable the manager from performing a layout
47857      * @param {Boolean} noLayout true to disable a layout update 
47858      */
47859     endUpdate : function(noLayout){
47860         this.updating = false;
47861         if(!noLayout){
47862             this.layout();
47863         }    
47864     },
47865     
47866     layout: function(){
47867         
47868     },
47869     
47870     onRegionResized : function(region, newSize){
47871         this.fireEvent("regionresized", region, newSize);
47872         this.layout();
47873     },
47874     
47875     onRegionCollapsed : function(region){
47876         this.fireEvent("regioncollapsed", region);
47877     },
47878     
47879     onRegionExpanded : function(region){
47880         this.fireEvent("regionexpanded", region);
47881     },
47882         
47883     /**
47884      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47885      * performs box-model adjustments.
47886      * @return {Object} The size as an object {width: (the width), height: (the height)}
47887      */
47888     getViewSize : function(){
47889         var size;
47890         if(this.el.dom != document.body){
47891             size = this.el.getSize();
47892         }else{
47893             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47894         }
47895         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47896         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47897         return size;
47898     },
47899     
47900     /**
47901      * Returns the Element this layout is bound to.
47902      * @return {Roo.Element}
47903      */
47904     getEl : function(){
47905         return this.el;
47906     },
47907     
47908     /**
47909      * Returns the specified region.
47910      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47911      * @return {Roo.LayoutRegion}
47912      */
47913     getRegion : function(target){
47914         return this.regions[target.toLowerCase()];
47915     },
47916     
47917     onWindowResize : function(){
47918         if(this.monitorWindowResize){
47919             this.layout();
47920         }
47921     }
47922 });/*
47923  * Based on:
47924  * Ext JS Library 1.1.1
47925  * Copyright(c) 2006-2007, Ext JS, LLC.
47926  *
47927  * Originally Released Under LGPL - original licence link has changed is not relivant.
47928  *
47929  * Fork - LGPL
47930  * <script type="text/javascript">
47931  */
47932 /**
47933  * @class Roo.BorderLayout
47934  * @extends Roo.LayoutManager
47935  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47936  * please see: <br><br>
47937  * <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>
47938  * <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>
47939  * Example:
47940  <pre><code>
47941  var layout = new Roo.BorderLayout(document.body, {
47942     north: {
47943         initialSize: 25,
47944         titlebar: false
47945     },
47946     west: {
47947         split:true,
47948         initialSize: 200,
47949         minSize: 175,
47950         maxSize: 400,
47951         titlebar: true,
47952         collapsible: true
47953     },
47954     east: {
47955         split:true,
47956         initialSize: 202,
47957         minSize: 175,
47958         maxSize: 400,
47959         titlebar: true,
47960         collapsible: true
47961     },
47962     south: {
47963         split:true,
47964         initialSize: 100,
47965         minSize: 100,
47966         maxSize: 200,
47967         titlebar: true,
47968         collapsible: true
47969     },
47970     center: {
47971         titlebar: true,
47972         autoScroll:true,
47973         resizeTabs: true,
47974         minTabWidth: 50,
47975         preferredTabWidth: 150
47976     }
47977 });
47978
47979 // shorthand
47980 var CP = Roo.ContentPanel;
47981
47982 layout.beginUpdate();
47983 layout.add("north", new CP("north", "North"));
47984 layout.add("south", new CP("south", {title: "South", closable: true}));
47985 layout.add("west", new CP("west", {title: "West"}));
47986 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
47987 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
47988 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
47989 layout.getRegion("center").showPanel("center1");
47990 layout.endUpdate();
47991 </code></pre>
47992
47993 <b>The container the layout is rendered into can be either the body element or any other element.
47994 If it is not the body element, the container needs to either be an absolute positioned element,
47995 or you will need to add "position:relative" to the css of the container.  You will also need to specify
47996 the container size if it is not the body element.</b>
47997
47998 * @constructor
47999 * Create a new BorderLayout
48000 * @param {String/HTMLElement/Element} container The container this layout is bound to
48001 * @param {Object} config Configuration options
48002  */
48003 Roo.BorderLayout = function(container, config){
48004     config = config || {};
48005     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48006     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48007     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48008         var target = this.factory.validRegions[i];
48009         if(config[target]){
48010             this.addRegion(target, config[target]);
48011         }
48012     }
48013 };
48014
48015 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48016     /**
48017      * Creates and adds a new region if it doesn't already exist.
48018      * @param {String} target The target region key (north, south, east, west or center).
48019      * @param {Object} config The regions config object
48020      * @return {BorderLayoutRegion} The new region
48021      */
48022     addRegion : function(target, config){
48023         if(!this.regions[target]){
48024             var r = this.factory.create(target, this, config);
48025             this.bindRegion(target, r);
48026         }
48027         return this.regions[target];
48028     },
48029
48030     // private (kinda)
48031     bindRegion : function(name, r){
48032         this.regions[name] = r;
48033         r.on("visibilitychange", this.layout, this);
48034         r.on("paneladded", this.layout, this);
48035         r.on("panelremoved", this.layout, this);
48036         r.on("invalidated", this.layout, this);
48037         r.on("resized", this.onRegionResized, this);
48038         r.on("collapsed", this.onRegionCollapsed, this);
48039         r.on("expanded", this.onRegionExpanded, this);
48040     },
48041
48042     /**
48043      * Performs a layout update.
48044      */
48045     layout : function(){
48046         if(this.updating) return;
48047         var size = this.getViewSize();
48048         var w = size.width;
48049         var h = size.height;
48050         var centerW = w;
48051         var centerH = h;
48052         var centerY = 0;
48053         var centerX = 0;
48054         //var x = 0, y = 0;
48055
48056         var rs = this.regions;
48057         var north = rs["north"];
48058         var south = rs["south"]; 
48059         var west = rs["west"];
48060         var east = rs["east"];
48061         var center = rs["center"];
48062         //if(this.hideOnLayout){ // not supported anymore
48063             //c.el.setStyle("display", "none");
48064         //}
48065         if(north && north.isVisible()){
48066             var b = north.getBox();
48067             var m = north.getMargins();
48068             b.width = w - (m.left+m.right);
48069             b.x = m.left;
48070             b.y = m.top;
48071             centerY = b.height + b.y + m.bottom;
48072             centerH -= centerY;
48073             north.updateBox(this.safeBox(b));
48074         }
48075         if(south && south.isVisible()){
48076             var b = south.getBox();
48077             var m = south.getMargins();
48078             b.width = w - (m.left+m.right);
48079             b.x = m.left;
48080             var totalHeight = (b.height + m.top + m.bottom);
48081             b.y = h - totalHeight + m.top;
48082             centerH -= totalHeight;
48083             south.updateBox(this.safeBox(b));
48084         }
48085         if(west && west.isVisible()){
48086             var b = west.getBox();
48087             var m = west.getMargins();
48088             b.height = centerH - (m.top+m.bottom);
48089             b.x = m.left;
48090             b.y = centerY + m.top;
48091             var totalWidth = (b.width + m.left + m.right);
48092             centerX += totalWidth;
48093             centerW -= totalWidth;
48094             west.updateBox(this.safeBox(b));
48095         }
48096         if(east && east.isVisible()){
48097             var b = east.getBox();
48098             var m = east.getMargins();
48099             b.height = centerH - (m.top+m.bottom);
48100             var totalWidth = (b.width + m.left + m.right);
48101             b.x = w - totalWidth + m.left;
48102             b.y = centerY + m.top;
48103             centerW -= totalWidth;
48104             east.updateBox(this.safeBox(b));
48105         }
48106         if(center){
48107             var m = center.getMargins();
48108             var centerBox = {
48109                 x: centerX + m.left,
48110                 y: centerY + m.top,
48111                 width: centerW - (m.left+m.right),
48112                 height: centerH - (m.top+m.bottom)
48113             };
48114             //if(this.hideOnLayout){
48115                 //center.el.setStyle("display", "block");
48116             //}
48117             center.updateBox(this.safeBox(centerBox));
48118         }
48119         this.el.repaint();
48120         this.fireEvent("layout", this);
48121     },
48122
48123     // private
48124     safeBox : function(box){
48125         box.width = Math.max(0, box.width);
48126         box.height = Math.max(0, box.height);
48127         return box;
48128     },
48129
48130     /**
48131      * Adds a ContentPanel (or subclass) to this layout.
48132      * @param {String} target The target region key (north, south, east, west or center).
48133      * @param {Roo.ContentPanel} panel The panel to add
48134      * @return {Roo.ContentPanel} The added panel
48135      */
48136     add : function(target, panel){
48137          
48138         target = target.toLowerCase();
48139         return this.regions[target].add(panel);
48140     },
48141
48142     /**
48143      * Remove a ContentPanel (or subclass) to this layout.
48144      * @param {String} target The target region key (north, south, east, west or center).
48145      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48146      * @return {Roo.ContentPanel} The removed panel
48147      */
48148     remove : function(target, panel){
48149         target = target.toLowerCase();
48150         return this.regions[target].remove(panel);
48151     },
48152
48153     /**
48154      * Searches all regions for a panel with the specified id
48155      * @param {String} panelId
48156      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48157      */
48158     findPanel : function(panelId){
48159         var rs = this.regions;
48160         for(var target in rs){
48161             if(typeof rs[target] != "function"){
48162                 var p = rs[target].getPanel(panelId);
48163                 if(p){
48164                     return p;
48165                 }
48166             }
48167         }
48168         return null;
48169     },
48170
48171     /**
48172      * Searches all regions for a panel with the specified id and activates (shows) it.
48173      * @param {String/ContentPanel} panelId The panels id or the panel itself
48174      * @return {Roo.ContentPanel} The shown panel or null
48175      */
48176     showPanel : function(panelId) {
48177       var rs = this.regions;
48178       for(var target in rs){
48179          var r = rs[target];
48180          if(typeof r != "function"){
48181             if(r.hasPanel(panelId)){
48182                return r.showPanel(panelId);
48183             }
48184          }
48185       }
48186       return null;
48187    },
48188
48189    /**
48190      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48191      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48192      */
48193     restoreState : function(provider){
48194         if(!provider){
48195             provider = Roo.state.Manager;
48196         }
48197         var sm = new Roo.LayoutStateManager();
48198         sm.init(this, provider);
48199     },
48200
48201     /**
48202      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48203      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48204      * a valid ContentPanel config object.  Example:
48205      * <pre><code>
48206 // Create the main layout
48207 var layout = new Roo.BorderLayout('main-ct', {
48208     west: {
48209         split:true,
48210         minSize: 175,
48211         titlebar: true
48212     },
48213     center: {
48214         title:'Components'
48215     }
48216 }, 'main-ct');
48217
48218 // Create and add multiple ContentPanels at once via configs
48219 layout.batchAdd({
48220    west: {
48221        id: 'source-files',
48222        autoCreate:true,
48223        title:'Ext Source Files',
48224        autoScroll:true,
48225        fitToFrame:true
48226    },
48227    center : {
48228        el: cview,
48229        autoScroll:true,
48230        fitToFrame:true,
48231        toolbar: tb,
48232        resizeEl:'cbody'
48233    }
48234 });
48235 </code></pre>
48236      * @param {Object} regions An object containing ContentPanel configs by region name
48237      */
48238     batchAdd : function(regions){
48239         this.beginUpdate();
48240         for(var rname in regions){
48241             var lr = this.regions[rname];
48242             if(lr){
48243                 this.addTypedPanels(lr, regions[rname]);
48244             }
48245         }
48246         this.endUpdate();
48247     },
48248
48249     // private
48250     addTypedPanels : function(lr, ps){
48251         if(typeof ps == 'string'){
48252             lr.add(new Roo.ContentPanel(ps));
48253         }
48254         else if(ps instanceof Array){
48255             for(var i =0, len = ps.length; i < len; i++){
48256                 this.addTypedPanels(lr, ps[i]);
48257             }
48258         }
48259         else if(!ps.events){ // raw config?
48260             var el = ps.el;
48261             delete ps.el; // prevent conflict
48262             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48263         }
48264         else {  // panel object assumed!
48265             lr.add(ps);
48266         }
48267     },
48268     /**
48269      * Adds a xtype elements to the layout.
48270      * <pre><code>
48271
48272 layout.addxtype({
48273        xtype : 'ContentPanel',
48274        region: 'west',
48275        items: [ .... ]
48276    }
48277 );
48278
48279 layout.addxtype({
48280         xtype : 'NestedLayoutPanel',
48281         region: 'west',
48282         layout: {
48283            center: { },
48284            west: { }   
48285         },
48286         items : [ ... list of content panels or nested layout panels.. ]
48287    }
48288 );
48289 </code></pre>
48290      * @param {Object} cfg Xtype definition of item to add.
48291      */
48292     addxtype : function(cfg)
48293     {
48294         // basically accepts a pannel...
48295         // can accept a layout region..!?!?
48296         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48297         
48298         if (!cfg.xtype.match(/Panel$/)) {
48299             return false;
48300         }
48301         var ret = false;
48302         
48303         if (typeof(cfg.region) == 'undefined') {
48304             Roo.log("Failed to add Panel, region was not set");
48305             Roo.log(cfg);
48306             return false;
48307         }
48308         var region = cfg.region;
48309         delete cfg.region;
48310         
48311           
48312         var xitems = [];
48313         if (cfg.items) {
48314             xitems = cfg.items;
48315             delete cfg.items;
48316         }
48317         var nb = false;
48318         
48319         switch(cfg.xtype) 
48320         {
48321             case 'ContentPanel':  // ContentPanel (el, cfg)
48322             case 'ScrollPanel':  // ContentPanel (el, cfg)
48323             case 'ViewPanel': 
48324                 if(cfg.autoCreate) {
48325                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48326                 } else {
48327                     var el = this.el.createChild();
48328                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48329                 }
48330                 
48331                 this.add(region, ret);
48332                 break;
48333             
48334             
48335             case 'TreePanel': // our new panel!
48336                 cfg.el = this.el.createChild();
48337                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48338                 this.add(region, ret);
48339                 break;
48340             
48341             case 'NestedLayoutPanel': 
48342                 // create a new Layout (which is  a Border Layout...
48343                 var el = this.el.createChild();
48344                 var clayout = cfg.layout;
48345                 delete cfg.layout;
48346                 clayout.items   = clayout.items  || [];
48347                 // replace this exitems with the clayout ones..
48348                 xitems = clayout.items;
48349                  
48350                 
48351                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48352                     cfg.background = false;
48353                 }
48354                 var layout = new Roo.BorderLayout(el, clayout);
48355                 
48356                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48357                 //console.log('adding nested layout panel '  + cfg.toSource());
48358                 this.add(region, ret);
48359                 nb = {}; /// find first...
48360                 break;
48361                 
48362             case 'GridPanel': 
48363             
48364                 // needs grid and region
48365                 
48366                 //var el = this.getRegion(region).el.createChild();
48367                 var el = this.el.createChild();
48368                 // create the grid first...
48369                 
48370                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48371                 delete cfg.grid;
48372                 if (region == 'center' && this.active ) {
48373                     cfg.background = false;
48374                 }
48375                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48376                 
48377                 this.add(region, ret);
48378                 if (cfg.background) {
48379                     ret.on('activate', function(gp) {
48380                         if (!gp.grid.rendered) {
48381                             gp.grid.render();
48382                         }
48383                     });
48384                 } else {
48385                     grid.render();
48386                 }
48387                 break;
48388            
48389            
48390            
48391                 
48392                 
48393                 
48394             default: 
48395                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48396                 return null;
48397              // GridPanel (grid, cfg)
48398             
48399         }
48400         this.beginUpdate();
48401         // add children..
48402         var region = '';
48403         var abn = {};
48404         Roo.each(xitems, function(i)  {
48405             region = nb && i.region ? i.region : false;
48406             
48407             var add = ret.addxtype(i);
48408            
48409             if (region) {
48410                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48411                 if (!i.background) {
48412                     abn[region] = nb[region] ;
48413                 }
48414             }
48415             
48416         });
48417         this.endUpdate();
48418
48419         // make the last non-background panel active..
48420         //if (nb) { Roo.log(abn); }
48421         if (nb) {
48422             
48423             for(var r in abn) {
48424                 region = this.getRegion(r);
48425                 if (region) {
48426                     // tried using nb[r], but it does not work..
48427                      
48428                     region.showPanel(abn[r]);
48429                    
48430                 }
48431             }
48432         }
48433         return ret;
48434         
48435     }
48436 });
48437
48438 /**
48439  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48440  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48441  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48442  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48443  * <pre><code>
48444 // shorthand
48445 var CP = Roo.ContentPanel;
48446
48447 var layout = Roo.BorderLayout.create({
48448     north: {
48449         initialSize: 25,
48450         titlebar: false,
48451         panels: [new CP("north", "North")]
48452     },
48453     west: {
48454         split:true,
48455         initialSize: 200,
48456         minSize: 175,
48457         maxSize: 400,
48458         titlebar: true,
48459         collapsible: true,
48460         panels: [new CP("west", {title: "West"})]
48461     },
48462     east: {
48463         split:true,
48464         initialSize: 202,
48465         minSize: 175,
48466         maxSize: 400,
48467         titlebar: true,
48468         collapsible: true,
48469         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48470     },
48471     south: {
48472         split:true,
48473         initialSize: 100,
48474         minSize: 100,
48475         maxSize: 200,
48476         titlebar: true,
48477         collapsible: true,
48478         panels: [new CP("south", {title: "South", closable: true})]
48479     },
48480     center: {
48481         titlebar: true,
48482         autoScroll:true,
48483         resizeTabs: true,
48484         minTabWidth: 50,
48485         preferredTabWidth: 150,
48486         panels: [
48487             new CP("center1", {title: "Close Me", closable: true}),
48488             new CP("center2", {title: "Center Panel", closable: false})
48489         ]
48490     }
48491 }, document.body);
48492
48493 layout.getRegion("center").showPanel("center1");
48494 </code></pre>
48495  * @param config
48496  * @param targetEl
48497  */
48498 Roo.BorderLayout.create = function(config, targetEl){
48499     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48500     layout.beginUpdate();
48501     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48502     for(var j = 0, jlen = regions.length; j < jlen; j++){
48503         var lr = regions[j];
48504         if(layout.regions[lr] && config[lr].panels){
48505             var r = layout.regions[lr];
48506             var ps = config[lr].panels;
48507             layout.addTypedPanels(r, ps);
48508         }
48509     }
48510     layout.endUpdate();
48511     return layout;
48512 };
48513
48514 // private
48515 Roo.BorderLayout.RegionFactory = {
48516     // private
48517     validRegions : ["north","south","east","west","center"],
48518
48519     // private
48520     create : function(target, mgr, config){
48521         target = target.toLowerCase();
48522         if(config.lightweight || config.basic){
48523             return new Roo.BasicLayoutRegion(mgr, config, target);
48524         }
48525         switch(target){
48526             case "north":
48527                 return new Roo.NorthLayoutRegion(mgr, config);
48528             case "south":
48529                 return new Roo.SouthLayoutRegion(mgr, config);
48530             case "east":
48531                 return new Roo.EastLayoutRegion(mgr, config);
48532             case "west":
48533                 return new Roo.WestLayoutRegion(mgr, config);
48534             case "center":
48535                 return new Roo.CenterLayoutRegion(mgr, config);
48536         }
48537         throw 'Layout region "'+target+'" not supported.';
48538     }
48539 };/*
48540  * Based on:
48541  * Ext JS Library 1.1.1
48542  * Copyright(c) 2006-2007, Ext JS, LLC.
48543  *
48544  * Originally Released Under LGPL - original licence link has changed is not relivant.
48545  *
48546  * Fork - LGPL
48547  * <script type="text/javascript">
48548  */
48549  
48550 /**
48551  * @class Roo.BasicLayoutRegion
48552  * @extends Roo.util.Observable
48553  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48554  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48555  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48556  */
48557 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48558     this.mgr = mgr;
48559     this.position  = pos;
48560     this.events = {
48561         /**
48562          * @scope Roo.BasicLayoutRegion
48563          */
48564         
48565         /**
48566          * @event beforeremove
48567          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48568          * @param {Roo.LayoutRegion} this
48569          * @param {Roo.ContentPanel} panel The panel
48570          * @param {Object} e The cancel event object
48571          */
48572         "beforeremove" : true,
48573         /**
48574          * @event invalidated
48575          * Fires when the layout for this region is changed.
48576          * @param {Roo.LayoutRegion} this
48577          */
48578         "invalidated" : true,
48579         /**
48580          * @event visibilitychange
48581          * Fires when this region is shown or hidden 
48582          * @param {Roo.LayoutRegion} this
48583          * @param {Boolean} visibility true or false
48584          */
48585         "visibilitychange" : true,
48586         /**
48587          * @event paneladded
48588          * Fires when a panel is added. 
48589          * @param {Roo.LayoutRegion} this
48590          * @param {Roo.ContentPanel} panel The panel
48591          */
48592         "paneladded" : true,
48593         /**
48594          * @event panelremoved
48595          * Fires when a panel is removed. 
48596          * @param {Roo.LayoutRegion} this
48597          * @param {Roo.ContentPanel} panel The panel
48598          */
48599         "panelremoved" : true,
48600         /**
48601          * @event collapsed
48602          * Fires when this region is collapsed.
48603          * @param {Roo.LayoutRegion} this
48604          */
48605         "collapsed" : true,
48606         /**
48607          * @event expanded
48608          * Fires when this region is expanded.
48609          * @param {Roo.LayoutRegion} this
48610          */
48611         "expanded" : true,
48612         /**
48613          * @event slideshow
48614          * Fires when this region is slid into view.
48615          * @param {Roo.LayoutRegion} this
48616          */
48617         "slideshow" : true,
48618         /**
48619          * @event slidehide
48620          * Fires when this region slides out of view. 
48621          * @param {Roo.LayoutRegion} this
48622          */
48623         "slidehide" : true,
48624         /**
48625          * @event panelactivated
48626          * Fires when a panel is activated. 
48627          * @param {Roo.LayoutRegion} this
48628          * @param {Roo.ContentPanel} panel The activated panel
48629          */
48630         "panelactivated" : true,
48631         /**
48632          * @event resized
48633          * Fires when the user resizes this region. 
48634          * @param {Roo.LayoutRegion} this
48635          * @param {Number} newSize The new size (width for east/west, height for north/south)
48636          */
48637         "resized" : true
48638     };
48639     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48640     this.panels = new Roo.util.MixedCollection();
48641     this.panels.getKey = this.getPanelId.createDelegate(this);
48642     this.box = null;
48643     this.activePanel = null;
48644     // ensure listeners are added...
48645     
48646     if (config.listeners || config.events) {
48647         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48648             listeners : config.listeners || {},
48649             events : config.events || {}
48650         });
48651     }
48652     
48653     if(skipConfig !== true){
48654         this.applyConfig(config);
48655     }
48656 };
48657
48658 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48659     getPanelId : function(p){
48660         return p.getId();
48661     },
48662     
48663     applyConfig : function(config){
48664         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48665         this.config = config;
48666         
48667     },
48668     
48669     /**
48670      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48671      * the width, for horizontal (north, south) the height.
48672      * @param {Number} newSize The new width or height
48673      */
48674     resizeTo : function(newSize){
48675         var el = this.el ? this.el :
48676                  (this.activePanel ? this.activePanel.getEl() : null);
48677         if(el){
48678             switch(this.position){
48679                 case "east":
48680                 case "west":
48681                     el.setWidth(newSize);
48682                     this.fireEvent("resized", this, newSize);
48683                 break;
48684                 case "north":
48685                 case "south":
48686                     el.setHeight(newSize);
48687                     this.fireEvent("resized", this, newSize);
48688                 break;                
48689             }
48690         }
48691     },
48692     
48693     getBox : function(){
48694         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48695     },
48696     
48697     getMargins : function(){
48698         return this.margins;
48699     },
48700     
48701     updateBox : function(box){
48702         this.box = box;
48703         var el = this.activePanel.getEl();
48704         el.dom.style.left = box.x + "px";
48705         el.dom.style.top = box.y + "px";
48706         this.activePanel.setSize(box.width, box.height);
48707     },
48708     
48709     /**
48710      * Returns the container element for this region.
48711      * @return {Roo.Element}
48712      */
48713     getEl : function(){
48714         return this.activePanel;
48715     },
48716     
48717     /**
48718      * Returns true if this region is currently visible.
48719      * @return {Boolean}
48720      */
48721     isVisible : function(){
48722         return this.activePanel ? true : false;
48723     },
48724     
48725     setActivePanel : function(panel){
48726         panel = this.getPanel(panel);
48727         if(this.activePanel && this.activePanel != panel){
48728             this.activePanel.setActiveState(false);
48729             this.activePanel.getEl().setLeftTop(-10000,-10000);
48730         }
48731         this.activePanel = panel;
48732         panel.setActiveState(true);
48733         if(this.box){
48734             panel.setSize(this.box.width, this.box.height);
48735         }
48736         this.fireEvent("panelactivated", this, panel);
48737         this.fireEvent("invalidated");
48738     },
48739     
48740     /**
48741      * Show the specified panel.
48742      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48743      * @return {Roo.ContentPanel} The shown panel or null
48744      */
48745     showPanel : function(panel){
48746         if(panel = this.getPanel(panel)){
48747             this.setActivePanel(panel);
48748         }
48749         return panel;
48750     },
48751     
48752     /**
48753      * Get the active panel for this region.
48754      * @return {Roo.ContentPanel} The active panel or null
48755      */
48756     getActivePanel : function(){
48757         return this.activePanel;
48758     },
48759     
48760     /**
48761      * Add the passed ContentPanel(s)
48762      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48763      * @return {Roo.ContentPanel} The panel added (if only one was added)
48764      */
48765     add : function(panel){
48766         if(arguments.length > 1){
48767             for(var i = 0, len = arguments.length; i < len; i++) {
48768                 this.add(arguments[i]);
48769             }
48770             return null;
48771         }
48772         if(this.hasPanel(panel)){
48773             this.showPanel(panel);
48774             return panel;
48775         }
48776         var el = panel.getEl();
48777         if(el.dom.parentNode != this.mgr.el.dom){
48778             this.mgr.el.dom.appendChild(el.dom);
48779         }
48780         if(panel.setRegion){
48781             panel.setRegion(this);
48782         }
48783         this.panels.add(panel);
48784         el.setStyle("position", "absolute");
48785         if(!panel.background){
48786             this.setActivePanel(panel);
48787             if(this.config.initialSize && this.panels.getCount()==1){
48788                 this.resizeTo(this.config.initialSize);
48789             }
48790         }
48791         this.fireEvent("paneladded", this, panel);
48792         return panel;
48793     },
48794     
48795     /**
48796      * Returns true if the panel is in this region.
48797      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48798      * @return {Boolean}
48799      */
48800     hasPanel : function(panel){
48801         if(typeof panel == "object"){ // must be panel obj
48802             panel = panel.getId();
48803         }
48804         return this.getPanel(panel) ? true : false;
48805     },
48806     
48807     /**
48808      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48809      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48810      * @param {Boolean} preservePanel Overrides the config preservePanel option
48811      * @return {Roo.ContentPanel} The panel that was removed
48812      */
48813     remove : function(panel, preservePanel){
48814         panel = this.getPanel(panel);
48815         if(!panel){
48816             return null;
48817         }
48818         var e = {};
48819         this.fireEvent("beforeremove", this, panel, e);
48820         if(e.cancel === true){
48821             return null;
48822         }
48823         var panelId = panel.getId();
48824         this.panels.removeKey(panelId);
48825         return panel;
48826     },
48827     
48828     /**
48829      * Returns the panel specified or null if it's not in this region.
48830      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48831      * @return {Roo.ContentPanel}
48832      */
48833     getPanel : function(id){
48834         if(typeof id == "object"){ // must be panel obj
48835             return id;
48836         }
48837         return this.panels.get(id);
48838     },
48839     
48840     /**
48841      * Returns this regions position (north/south/east/west/center).
48842      * @return {String} 
48843      */
48844     getPosition: function(){
48845         return this.position;    
48846     }
48847 });/*
48848  * Based on:
48849  * Ext JS Library 1.1.1
48850  * Copyright(c) 2006-2007, Ext JS, LLC.
48851  *
48852  * Originally Released Under LGPL - original licence link has changed is not relivant.
48853  *
48854  * Fork - LGPL
48855  * <script type="text/javascript">
48856  */
48857  
48858 /**
48859  * @class Roo.LayoutRegion
48860  * @extends Roo.BasicLayoutRegion
48861  * This class represents a region in a layout manager.
48862  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48863  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48864  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48865  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48866  * @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})
48867  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48868  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48869  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48870  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48871  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48872  * @cfg {String}    title           The title for the region (overrides panel titles)
48873  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48874  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48875  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48876  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48877  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48878  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48879  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48880  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48881  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48882  * @cfg {Boolean}   showPin         True to show a pin button
48883  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48884  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48885  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48886  * @cfg {Number}    width           For East/West panels
48887  * @cfg {Number}    height          For North/South panels
48888  * @cfg {Boolean}   split           To show the splitter
48889  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48890  */
48891 Roo.LayoutRegion = function(mgr, config, pos){
48892     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48893     var dh = Roo.DomHelper;
48894     /** This region's container element 
48895     * @type Roo.Element */
48896     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48897     /** This region's title element 
48898     * @type Roo.Element */
48899
48900     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48901         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48902         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48903     ]}, true);
48904     this.titleEl.enableDisplayMode();
48905     /** This region's title text element 
48906     * @type HTMLElement */
48907     this.titleTextEl = this.titleEl.dom.firstChild;
48908     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48909     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48910     this.closeBtn.enableDisplayMode();
48911     this.closeBtn.on("click", this.closeClicked, this);
48912     this.closeBtn.hide();
48913
48914     this.createBody(config);
48915     this.visible = true;
48916     this.collapsed = false;
48917
48918     if(config.hideWhenEmpty){
48919         this.hide();
48920         this.on("paneladded", this.validateVisibility, this);
48921         this.on("panelremoved", this.validateVisibility, this);
48922     }
48923     this.applyConfig(config);
48924 };
48925
48926 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48927
48928     createBody : function(){
48929         /** This region's body element 
48930         * @type Roo.Element */
48931         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48932     },
48933
48934     applyConfig : function(c){
48935         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48936             var dh = Roo.DomHelper;
48937             if(c.titlebar !== false){
48938                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48939                 this.collapseBtn.on("click", this.collapse, this);
48940                 this.collapseBtn.enableDisplayMode();
48941
48942                 if(c.showPin === true || this.showPin){
48943                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48944                     this.stickBtn.enableDisplayMode();
48945                     this.stickBtn.on("click", this.expand, this);
48946                     this.stickBtn.hide();
48947                 }
48948             }
48949             /** This region's collapsed element
48950             * @type Roo.Element */
48951             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48952                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48953             ]}, true);
48954             if(c.floatable !== false){
48955                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48956                this.collapsedEl.on("click", this.collapseClick, this);
48957             }
48958
48959             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48960                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48961                    id: "message", unselectable: "on", style:{"float":"left"}});
48962                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48963              }
48964             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48965             this.expandBtn.on("click", this.expand, this);
48966         }
48967         if(this.collapseBtn){
48968             this.collapseBtn.setVisible(c.collapsible == true);
48969         }
48970         this.cmargins = c.cmargins || this.cmargins ||
48971                          (this.position == "west" || this.position == "east" ?
48972                              {top: 0, left: 2, right:2, bottom: 0} :
48973                              {top: 2, left: 0, right:0, bottom: 2});
48974         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48975         this.bottomTabs = c.tabPosition != "top";
48976         this.autoScroll = c.autoScroll || false;
48977         if(this.autoScroll){
48978             this.bodyEl.setStyle("overflow", "auto");
48979         }else{
48980             this.bodyEl.setStyle("overflow", "hidden");
48981         }
48982         //if(c.titlebar !== false){
48983             if((!c.titlebar && !c.title) || c.titlebar === false){
48984                 this.titleEl.hide();
48985             }else{
48986                 this.titleEl.show();
48987                 if(c.title){
48988                     this.titleTextEl.innerHTML = c.title;
48989                 }
48990             }
48991         //}
48992         this.duration = c.duration || .30;
48993         this.slideDuration = c.slideDuration || .45;
48994         this.config = c;
48995         if(c.collapsed){
48996             this.collapse(true);
48997         }
48998         if(c.hidden){
48999             this.hide();
49000         }
49001     },
49002     /**
49003      * Returns true if this region is currently visible.
49004      * @return {Boolean}
49005      */
49006     isVisible : function(){
49007         return this.visible;
49008     },
49009
49010     /**
49011      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49012      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49013      */
49014     setCollapsedTitle : function(title){
49015         title = title || "&#160;";
49016         if(this.collapsedTitleTextEl){
49017             this.collapsedTitleTextEl.innerHTML = title;
49018         }
49019     },
49020
49021     getBox : function(){
49022         var b;
49023         if(!this.collapsed){
49024             b = this.el.getBox(false, true);
49025         }else{
49026             b = this.collapsedEl.getBox(false, true);
49027         }
49028         return b;
49029     },
49030
49031     getMargins : function(){
49032         return this.collapsed ? this.cmargins : this.margins;
49033     },
49034
49035     highlight : function(){
49036         this.el.addClass("x-layout-panel-dragover");
49037     },
49038
49039     unhighlight : function(){
49040         this.el.removeClass("x-layout-panel-dragover");
49041     },
49042
49043     updateBox : function(box){
49044         this.box = box;
49045         if(!this.collapsed){
49046             this.el.dom.style.left = box.x + "px";
49047             this.el.dom.style.top = box.y + "px";
49048             this.updateBody(box.width, box.height);
49049         }else{
49050             this.collapsedEl.dom.style.left = box.x + "px";
49051             this.collapsedEl.dom.style.top = box.y + "px";
49052             this.collapsedEl.setSize(box.width, box.height);
49053         }
49054         if(this.tabs){
49055             this.tabs.autoSizeTabs();
49056         }
49057     },
49058
49059     updateBody : function(w, h){
49060         if(w !== null){
49061             this.el.setWidth(w);
49062             w -= this.el.getBorderWidth("rl");
49063             if(this.config.adjustments){
49064                 w += this.config.adjustments[0];
49065             }
49066         }
49067         if(h !== null){
49068             this.el.setHeight(h);
49069             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49070             h -= this.el.getBorderWidth("tb");
49071             if(this.config.adjustments){
49072                 h += this.config.adjustments[1];
49073             }
49074             this.bodyEl.setHeight(h);
49075             if(this.tabs){
49076                 h = this.tabs.syncHeight(h);
49077             }
49078         }
49079         if(this.panelSize){
49080             w = w !== null ? w : this.panelSize.width;
49081             h = h !== null ? h : this.panelSize.height;
49082         }
49083         if(this.activePanel){
49084             var el = this.activePanel.getEl();
49085             w = w !== null ? w : el.getWidth();
49086             h = h !== null ? h : el.getHeight();
49087             this.panelSize = {width: w, height: h};
49088             this.activePanel.setSize(w, h);
49089         }
49090         if(Roo.isIE && this.tabs){
49091             this.tabs.el.repaint();
49092         }
49093     },
49094
49095     /**
49096      * Returns the container element for this region.
49097      * @return {Roo.Element}
49098      */
49099     getEl : function(){
49100         return this.el;
49101     },
49102
49103     /**
49104      * Hides this region.
49105      */
49106     hide : function(){
49107         if(!this.collapsed){
49108             this.el.dom.style.left = "-2000px";
49109             this.el.hide();
49110         }else{
49111             this.collapsedEl.dom.style.left = "-2000px";
49112             this.collapsedEl.hide();
49113         }
49114         this.visible = false;
49115         this.fireEvent("visibilitychange", this, false);
49116     },
49117
49118     /**
49119      * Shows this region if it was previously hidden.
49120      */
49121     show : function(){
49122         if(!this.collapsed){
49123             this.el.show();
49124         }else{
49125             this.collapsedEl.show();
49126         }
49127         this.visible = true;
49128         this.fireEvent("visibilitychange", this, true);
49129     },
49130
49131     closeClicked : function(){
49132         if(this.activePanel){
49133             this.remove(this.activePanel);
49134         }
49135     },
49136
49137     collapseClick : function(e){
49138         if(this.isSlid){
49139            e.stopPropagation();
49140            this.slideIn();
49141         }else{
49142            e.stopPropagation();
49143            this.slideOut();
49144         }
49145     },
49146
49147     /**
49148      * Collapses this region.
49149      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49150      */
49151     collapse : function(skipAnim){
49152         if(this.collapsed) return;
49153         this.collapsed = true;
49154         if(this.split){
49155             this.split.el.hide();
49156         }
49157         if(this.config.animate && skipAnim !== true){
49158             this.fireEvent("invalidated", this);
49159             this.animateCollapse();
49160         }else{
49161             this.el.setLocation(-20000,-20000);
49162             this.el.hide();
49163             this.collapsedEl.show();
49164             this.fireEvent("collapsed", this);
49165             this.fireEvent("invalidated", this);
49166         }
49167     },
49168
49169     animateCollapse : function(){
49170         // overridden
49171     },
49172
49173     /**
49174      * Expands this region if it was previously collapsed.
49175      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49176      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49177      */
49178     expand : function(e, skipAnim){
49179         if(e) e.stopPropagation();
49180         if(!this.collapsed || this.el.hasActiveFx()) return;
49181         if(this.isSlid){
49182             this.afterSlideIn();
49183             skipAnim = true;
49184         }
49185         this.collapsed = false;
49186         if(this.config.animate && skipAnim !== true){
49187             this.animateExpand();
49188         }else{
49189             this.el.show();
49190             if(this.split){
49191                 this.split.el.show();
49192             }
49193             this.collapsedEl.setLocation(-2000,-2000);
49194             this.collapsedEl.hide();
49195             this.fireEvent("invalidated", this);
49196             this.fireEvent("expanded", this);
49197         }
49198     },
49199
49200     animateExpand : function(){
49201         // overridden
49202     },
49203
49204     initTabs : function()
49205     {
49206         this.bodyEl.setStyle("overflow", "hidden");
49207         var ts = new Roo.TabPanel(
49208                 this.bodyEl.dom,
49209                 {
49210                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49211                     disableTooltips: this.config.disableTabTips,
49212                     toolbar : this.config.toolbar
49213                 }
49214         );
49215         if(this.config.hideTabs){
49216             ts.stripWrap.setDisplayed(false);
49217         }
49218         this.tabs = ts;
49219         ts.resizeTabs = this.config.resizeTabs === true;
49220         ts.minTabWidth = this.config.minTabWidth || 40;
49221         ts.maxTabWidth = this.config.maxTabWidth || 250;
49222         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49223         ts.monitorResize = false;
49224         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49225         ts.bodyEl.addClass('x-layout-tabs-body');
49226         this.panels.each(this.initPanelAsTab, this);
49227     },
49228
49229     initPanelAsTab : function(panel){
49230         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49231                     this.config.closeOnTab && panel.isClosable());
49232         if(panel.tabTip !== undefined){
49233             ti.setTooltip(panel.tabTip);
49234         }
49235         ti.on("activate", function(){
49236               this.setActivePanel(panel);
49237         }, this);
49238         if(this.config.closeOnTab){
49239             ti.on("beforeclose", function(t, e){
49240                 e.cancel = true;
49241                 this.remove(panel);
49242             }, this);
49243         }
49244         return ti;
49245     },
49246
49247     updatePanelTitle : function(panel, title){
49248         if(this.activePanel == panel){
49249             this.updateTitle(title);
49250         }
49251         if(this.tabs){
49252             var ti = this.tabs.getTab(panel.getEl().id);
49253             ti.setText(title);
49254             if(panel.tabTip !== undefined){
49255                 ti.setTooltip(panel.tabTip);
49256             }
49257         }
49258     },
49259
49260     updateTitle : function(title){
49261         if(this.titleTextEl && !this.config.title){
49262             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49263         }
49264     },
49265
49266     setActivePanel : function(panel){
49267         panel = this.getPanel(panel);
49268         if(this.activePanel && this.activePanel != panel){
49269             this.activePanel.setActiveState(false);
49270         }
49271         this.activePanel = panel;
49272         panel.setActiveState(true);
49273         if(this.panelSize){
49274             panel.setSize(this.panelSize.width, this.panelSize.height);
49275         }
49276         if(this.closeBtn){
49277             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49278         }
49279         this.updateTitle(panel.getTitle());
49280         if(this.tabs){
49281             this.fireEvent("invalidated", this);
49282         }
49283         this.fireEvent("panelactivated", this, panel);
49284     },
49285
49286     /**
49287      * Shows the specified panel.
49288      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49289      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49290      */
49291     showPanel : function(panel){
49292         if(panel = this.getPanel(panel)){
49293             if(this.tabs){
49294                 var tab = this.tabs.getTab(panel.getEl().id);
49295                 if(tab.isHidden()){
49296                     this.tabs.unhideTab(tab.id);
49297                 }
49298                 tab.activate();
49299             }else{
49300                 this.setActivePanel(panel);
49301             }
49302         }
49303         return panel;
49304     },
49305
49306     /**
49307      * Get the active panel for this region.
49308      * @return {Roo.ContentPanel} The active panel or null
49309      */
49310     getActivePanel : function(){
49311         return this.activePanel;
49312     },
49313
49314     validateVisibility : function(){
49315         if(this.panels.getCount() < 1){
49316             this.updateTitle("&#160;");
49317             this.closeBtn.hide();
49318             this.hide();
49319         }else{
49320             if(!this.isVisible()){
49321                 this.show();
49322             }
49323         }
49324     },
49325
49326     /**
49327      * Adds the passed ContentPanel(s) to this region.
49328      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49329      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49330      */
49331     add : function(panel){
49332         if(arguments.length > 1){
49333             for(var i = 0, len = arguments.length; i < len; i++) {
49334                 this.add(arguments[i]);
49335             }
49336             return null;
49337         }
49338         if(this.hasPanel(panel)){
49339             this.showPanel(panel);
49340             return panel;
49341         }
49342         panel.setRegion(this);
49343         this.panels.add(panel);
49344         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49345             this.bodyEl.dom.appendChild(panel.getEl().dom);
49346             if(panel.background !== true){
49347                 this.setActivePanel(panel);
49348             }
49349             this.fireEvent("paneladded", this, panel);
49350             return panel;
49351         }
49352         if(!this.tabs){
49353             this.initTabs();
49354         }else{
49355             this.initPanelAsTab(panel);
49356         }
49357         if(panel.background !== true){
49358             this.tabs.activate(panel.getEl().id);
49359         }
49360         this.fireEvent("paneladded", this, panel);
49361         return panel;
49362     },
49363
49364     /**
49365      * Hides the tab for the specified panel.
49366      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49367      */
49368     hidePanel : function(panel){
49369         if(this.tabs && (panel = this.getPanel(panel))){
49370             this.tabs.hideTab(panel.getEl().id);
49371         }
49372     },
49373
49374     /**
49375      * Unhides the tab for a previously hidden panel.
49376      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49377      */
49378     unhidePanel : function(panel){
49379         if(this.tabs && (panel = this.getPanel(panel))){
49380             this.tabs.unhideTab(panel.getEl().id);
49381         }
49382     },
49383
49384     clearPanels : function(){
49385         while(this.panels.getCount() > 0){
49386              this.remove(this.panels.first());
49387         }
49388     },
49389
49390     /**
49391      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49392      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49393      * @param {Boolean} preservePanel Overrides the config preservePanel option
49394      * @return {Roo.ContentPanel} The panel that was removed
49395      */
49396     remove : function(panel, preservePanel){
49397         panel = this.getPanel(panel);
49398         if(!panel){
49399             return null;
49400         }
49401         var e = {};
49402         this.fireEvent("beforeremove", this, panel, e);
49403         if(e.cancel === true){
49404             return null;
49405         }
49406         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49407         var panelId = panel.getId();
49408         this.panels.removeKey(panelId);
49409         if(preservePanel){
49410             document.body.appendChild(panel.getEl().dom);
49411         }
49412         if(this.tabs){
49413             this.tabs.removeTab(panel.getEl().id);
49414         }else if (!preservePanel){
49415             this.bodyEl.dom.removeChild(panel.getEl().dom);
49416         }
49417         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49418             var p = this.panels.first();
49419             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49420             tempEl.appendChild(p.getEl().dom);
49421             this.bodyEl.update("");
49422             this.bodyEl.dom.appendChild(p.getEl().dom);
49423             tempEl = null;
49424             this.updateTitle(p.getTitle());
49425             this.tabs = null;
49426             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49427             this.setActivePanel(p);
49428         }
49429         panel.setRegion(null);
49430         if(this.activePanel == panel){
49431             this.activePanel = null;
49432         }
49433         if(this.config.autoDestroy !== false && preservePanel !== true){
49434             try{panel.destroy();}catch(e){}
49435         }
49436         this.fireEvent("panelremoved", this, panel);
49437         return panel;
49438     },
49439
49440     /**
49441      * Returns the TabPanel component used by this region
49442      * @return {Roo.TabPanel}
49443      */
49444     getTabs : function(){
49445         return this.tabs;
49446     },
49447
49448     createTool : function(parentEl, className){
49449         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49450             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49451         btn.addClassOnOver("x-layout-tools-button-over");
49452         return btn;
49453     }
49454 });/*
49455  * Based on:
49456  * Ext JS Library 1.1.1
49457  * Copyright(c) 2006-2007, Ext JS, LLC.
49458  *
49459  * Originally Released Under LGPL - original licence link has changed is not relivant.
49460  *
49461  * Fork - LGPL
49462  * <script type="text/javascript">
49463  */
49464  
49465
49466
49467 /**
49468  * @class Roo.SplitLayoutRegion
49469  * @extends Roo.LayoutRegion
49470  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49471  */
49472 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49473     this.cursor = cursor;
49474     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49475 };
49476
49477 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49478     splitTip : "Drag to resize.",
49479     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49480     useSplitTips : false,
49481
49482     applyConfig : function(config){
49483         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49484         if(config.split){
49485             if(!this.split){
49486                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49487                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49488                 /** The SplitBar for this region 
49489                 * @type Roo.SplitBar */
49490                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49491                 this.split.on("moved", this.onSplitMove, this);
49492                 this.split.useShim = config.useShim === true;
49493                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49494                 if(this.useSplitTips){
49495                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49496                 }
49497                 if(config.collapsible){
49498                     this.split.el.on("dblclick", this.collapse,  this);
49499                 }
49500             }
49501             if(typeof config.minSize != "undefined"){
49502                 this.split.minSize = config.minSize;
49503             }
49504             if(typeof config.maxSize != "undefined"){
49505                 this.split.maxSize = config.maxSize;
49506             }
49507             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49508                 this.hideSplitter();
49509             }
49510         }
49511     },
49512
49513     getHMaxSize : function(){
49514          var cmax = this.config.maxSize || 10000;
49515          var center = this.mgr.getRegion("center");
49516          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49517     },
49518
49519     getVMaxSize : function(){
49520          var cmax = this.config.maxSize || 10000;
49521          var center = this.mgr.getRegion("center");
49522          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49523     },
49524
49525     onSplitMove : function(split, newSize){
49526         this.fireEvent("resized", this, newSize);
49527     },
49528     
49529     /** 
49530      * Returns the {@link Roo.SplitBar} for this region.
49531      * @return {Roo.SplitBar}
49532      */
49533     getSplitBar : function(){
49534         return this.split;
49535     },
49536     
49537     hide : function(){
49538         this.hideSplitter();
49539         Roo.SplitLayoutRegion.superclass.hide.call(this);
49540     },
49541
49542     hideSplitter : function(){
49543         if(this.split){
49544             this.split.el.setLocation(-2000,-2000);
49545             this.split.el.hide();
49546         }
49547     },
49548
49549     show : function(){
49550         if(this.split){
49551             this.split.el.show();
49552         }
49553         Roo.SplitLayoutRegion.superclass.show.call(this);
49554     },
49555     
49556     beforeSlide: function(){
49557         if(Roo.isGecko){// firefox overflow auto bug workaround
49558             this.bodyEl.clip();
49559             if(this.tabs) this.tabs.bodyEl.clip();
49560             if(this.activePanel){
49561                 this.activePanel.getEl().clip();
49562                 
49563                 if(this.activePanel.beforeSlide){
49564                     this.activePanel.beforeSlide();
49565                 }
49566             }
49567         }
49568     },
49569     
49570     afterSlide : function(){
49571         if(Roo.isGecko){// firefox overflow auto bug workaround
49572             this.bodyEl.unclip();
49573             if(this.tabs) this.tabs.bodyEl.unclip();
49574             if(this.activePanel){
49575                 this.activePanel.getEl().unclip();
49576                 if(this.activePanel.afterSlide){
49577                     this.activePanel.afterSlide();
49578                 }
49579             }
49580         }
49581     },
49582
49583     initAutoHide : function(){
49584         if(this.autoHide !== false){
49585             if(!this.autoHideHd){
49586                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49587                 this.autoHideHd = {
49588                     "mouseout": function(e){
49589                         if(!e.within(this.el, true)){
49590                             st.delay(500);
49591                         }
49592                     },
49593                     "mouseover" : function(e){
49594                         st.cancel();
49595                     },
49596                     scope : this
49597                 };
49598             }
49599             this.el.on(this.autoHideHd);
49600         }
49601     },
49602
49603     clearAutoHide : function(){
49604         if(this.autoHide !== false){
49605             this.el.un("mouseout", this.autoHideHd.mouseout);
49606             this.el.un("mouseover", this.autoHideHd.mouseover);
49607         }
49608     },
49609
49610     clearMonitor : function(){
49611         Roo.get(document).un("click", this.slideInIf, this);
49612     },
49613
49614     // these names are backwards but not changed for compat
49615     slideOut : function(){
49616         if(this.isSlid || this.el.hasActiveFx()){
49617             return;
49618         }
49619         this.isSlid = true;
49620         if(this.collapseBtn){
49621             this.collapseBtn.hide();
49622         }
49623         this.closeBtnState = this.closeBtn.getStyle('display');
49624         this.closeBtn.hide();
49625         if(this.stickBtn){
49626             this.stickBtn.show();
49627         }
49628         this.el.show();
49629         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49630         this.beforeSlide();
49631         this.el.setStyle("z-index", 10001);
49632         this.el.slideIn(this.getSlideAnchor(), {
49633             callback: function(){
49634                 this.afterSlide();
49635                 this.initAutoHide();
49636                 Roo.get(document).on("click", this.slideInIf, this);
49637                 this.fireEvent("slideshow", this);
49638             },
49639             scope: this,
49640             block: true
49641         });
49642     },
49643
49644     afterSlideIn : function(){
49645         this.clearAutoHide();
49646         this.isSlid = false;
49647         this.clearMonitor();
49648         this.el.setStyle("z-index", "");
49649         if(this.collapseBtn){
49650             this.collapseBtn.show();
49651         }
49652         this.closeBtn.setStyle('display', this.closeBtnState);
49653         if(this.stickBtn){
49654             this.stickBtn.hide();
49655         }
49656         this.fireEvent("slidehide", this);
49657     },
49658
49659     slideIn : function(cb){
49660         if(!this.isSlid || this.el.hasActiveFx()){
49661             Roo.callback(cb);
49662             return;
49663         }
49664         this.isSlid = false;
49665         this.beforeSlide();
49666         this.el.slideOut(this.getSlideAnchor(), {
49667             callback: function(){
49668                 this.el.setLeftTop(-10000, -10000);
49669                 this.afterSlide();
49670                 this.afterSlideIn();
49671                 Roo.callback(cb);
49672             },
49673             scope: this,
49674             block: true
49675         });
49676     },
49677     
49678     slideInIf : function(e){
49679         if(!e.within(this.el)){
49680             this.slideIn();
49681         }
49682     },
49683
49684     animateCollapse : function(){
49685         this.beforeSlide();
49686         this.el.setStyle("z-index", 20000);
49687         var anchor = this.getSlideAnchor();
49688         this.el.slideOut(anchor, {
49689             callback : function(){
49690                 this.el.setStyle("z-index", "");
49691                 this.collapsedEl.slideIn(anchor, {duration:.3});
49692                 this.afterSlide();
49693                 this.el.setLocation(-10000,-10000);
49694                 this.el.hide();
49695                 this.fireEvent("collapsed", this);
49696             },
49697             scope: this,
49698             block: true
49699         });
49700     },
49701
49702     animateExpand : function(){
49703         this.beforeSlide();
49704         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49705         this.el.setStyle("z-index", 20000);
49706         this.collapsedEl.hide({
49707             duration:.1
49708         });
49709         this.el.slideIn(this.getSlideAnchor(), {
49710             callback : function(){
49711                 this.el.setStyle("z-index", "");
49712                 this.afterSlide();
49713                 if(this.split){
49714                     this.split.el.show();
49715                 }
49716                 this.fireEvent("invalidated", this);
49717                 this.fireEvent("expanded", this);
49718             },
49719             scope: this,
49720             block: true
49721         });
49722     },
49723
49724     anchors : {
49725         "west" : "left",
49726         "east" : "right",
49727         "north" : "top",
49728         "south" : "bottom"
49729     },
49730
49731     sanchors : {
49732         "west" : "l",
49733         "east" : "r",
49734         "north" : "t",
49735         "south" : "b"
49736     },
49737
49738     canchors : {
49739         "west" : "tl-tr",
49740         "east" : "tr-tl",
49741         "north" : "tl-bl",
49742         "south" : "bl-tl"
49743     },
49744
49745     getAnchor : function(){
49746         return this.anchors[this.position];
49747     },
49748
49749     getCollapseAnchor : function(){
49750         return this.canchors[this.position];
49751     },
49752
49753     getSlideAnchor : function(){
49754         return this.sanchors[this.position];
49755     },
49756
49757     getAlignAdj : function(){
49758         var cm = this.cmargins;
49759         switch(this.position){
49760             case "west":
49761                 return [0, 0];
49762             break;
49763             case "east":
49764                 return [0, 0];
49765             break;
49766             case "north":
49767                 return [0, 0];
49768             break;
49769             case "south":
49770                 return [0, 0];
49771             break;
49772         }
49773     },
49774
49775     getExpandAdj : function(){
49776         var c = this.collapsedEl, cm = this.cmargins;
49777         switch(this.position){
49778             case "west":
49779                 return [-(cm.right+c.getWidth()+cm.left), 0];
49780             break;
49781             case "east":
49782                 return [cm.right+c.getWidth()+cm.left, 0];
49783             break;
49784             case "north":
49785                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49786             break;
49787             case "south":
49788                 return [0, cm.top+cm.bottom+c.getHeight()];
49789             break;
49790         }
49791     }
49792 });/*
49793  * Based on:
49794  * Ext JS Library 1.1.1
49795  * Copyright(c) 2006-2007, Ext JS, LLC.
49796  *
49797  * Originally Released Under LGPL - original licence link has changed is not relivant.
49798  *
49799  * Fork - LGPL
49800  * <script type="text/javascript">
49801  */
49802 /*
49803  * These classes are private internal classes
49804  */
49805 Roo.CenterLayoutRegion = function(mgr, config){
49806     Roo.LayoutRegion.call(this, mgr, config, "center");
49807     this.visible = true;
49808     this.minWidth = config.minWidth || 20;
49809     this.minHeight = config.minHeight || 20;
49810 };
49811
49812 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49813     hide : function(){
49814         // center panel can't be hidden
49815     },
49816     
49817     show : function(){
49818         // center panel can't be hidden
49819     },
49820     
49821     getMinWidth: function(){
49822         return this.minWidth;
49823     },
49824     
49825     getMinHeight: function(){
49826         return this.minHeight;
49827     }
49828 });
49829
49830
49831 Roo.NorthLayoutRegion = function(mgr, config){
49832     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49833     if(this.split){
49834         this.split.placement = Roo.SplitBar.TOP;
49835         this.split.orientation = Roo.SplitBar.VERTICAL;
49836         this.split.el.addClass("x-layout-split-v");
49837     }
49838     var size = config.initialSize || config.height;
49839     if(typeof size != "undefined"){
49840         this.el.setHeight(size);
49841     }
49842 };
49843 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49844     orientation: Roo.SplitBar.VERTICAL,
49845     getBox : function(){
49846         if(this.collapsed){
49847             return this.collapsedEl.getBox();
49848         }
49849         var box = this.el.getBox();
49850         if(this.split){
49851             box.height += this.split.el.getHeight();
49852         }
49853         return box;
49854     },
49855     
49856     updateBox : function(box){
49857         if(this.split && !this.collapsed){
49858             box.height -= this.split.el.getHeight();
49859             this.split.el.setLeft(box.x);
49860             this.split.el.setTop(box.y+box.height);
49861             this.split.el.setWidth(box.width);
49862         }
49863         if(this.collapsed){
49864             this.updateBody(box.width, null);
49865         }
49866         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49867     }
49868 });
49869
49870 Roo.SouthLayoutRegion = function(mgr, config){
49871     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49872     if(this.split){
49873         this.split.placement = Roo.SplitBar.BOTTOM;
49874         this.split.orientation = Roo.SplitBar.VERTICAL;
49875         this.split.el.addClass("x-layout-split-v");
49876     }
49877     var size = config.initialSize || config.height;
49878     if(typeof size != "undefined"){
49879         this.el.setHeight(size);
49880     }
49881 };
49882 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49883     orientation: Roo.SplitBar.VERTICAL,
49884     getBox : function(){
49885         if(this.collapsed){
49886             return this.collapsedEl.getBox();
49887         }
49888         var box = this.el.getBox();
49889         if(this.split){
49890             var sh = this.split.el.getHeight();
49891             box.height += sh;
49892             box.y -= sh;
49893         }
49894         return box;
49895     },
49896     
49897     updateBox : function(box){
49898         if(this.split && !this.collapsed){
49899             var sh = this.split.el.getHeight();
49900             box.height -= sh;
49901             box.y += sh;
49902             this.split.el.setLeft(box.x);
49903             this.split.el.setTop(box.y-sh);
49904             this.split.el.setWidth(box.width);
49905         }
49906         if(this.collapsed){
49907             this.updateBody(box.width, null);
49908         }
49909         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49910     }
49911 });
49912
49913 Roo.EastLayoutRegion = function(mgr, config){
49914     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49915     if(this.split){
49916         this.split.placement = Roo.SplitBar.RIGHT;
49917         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49918         this.split.el.addClass("x-layout-split-h");
49919     }
49920     var size = config.initialSize || config.width;
49921     if(typeof size != "undefined"){
49922         this.el.setWidth(size);
49923     }
49924 };
49925 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49926     orientation: Roo.SplitBar.HORIZONTAL,
49927     getBox : function(){
49928         if(this.collapsed){
49929             return this.collapsedEl.getBox();
49930         }
49931         var box = this.el.getBox();
49932         if(this.split){
49933             var sw = this.split.el.getWidth();
49934             box.width += sw;
49935             box.x -= sw;
49936         }
49937         return box;
49938     },
49939
49940     updateBox : function(box){
49941         if(this.split && !this.collapsed){
49942             var sw = this.split.el.getWidth();
49943             box.width -= sw;
49944             this.split.el.setLeft(box.x);
49945             this.split.el.setTop(box.y);
49946             this.split.el.setHeight(box.height);
49947             box.x += sw;
49948         }
49949         if(this.collapsed){
49950             this.updateBody(null, box.height);
49951         }
49952         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49953     }
49954 });
49955
49956 Roo.WestLayoutRegion = function(mgr, config){
49957     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49958     if(this.split){
49959         this.split.placement = Roo.SplitBar.LEFT;
49960         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49961         this.split.el.addClass("x-layout-split-h");
49962     }
49963     var size = config.initialSize || config.width;
49964     if(typeof size != "undefined"){
49965         this.el.setWidth(size);
49966     }
49967 };
49968 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49969     orientation: Roo.SplitBar.HORIZONTAL,
49970     getBox : function(){
49971         if(this.collapsed){
49972             return this.collapsedEl.getBox();
49973         }
49974         var box = this.el.getBox();
49975         if(this.split){
49976             box.width += this.split.el.getWidth();
49977         }
49978         return box;
49979     },
49980     
49981     updateBox : function(box){
49982         if(this.split && !this.collapsed){
49983             var sw = this.split.el.getWidth();
49984             box.width -= sw;
49985             this.split.el.setLeft(box.x+box.width);
49986             this.split.el.setTop(box.y);
49987             this.split.el.setHeight(box.height);
49988         }
49989         if(this.collapsed){
49990             this.updateBody(null, box.height);
49991         }
49992         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49993     }
49994 });
49995 /*
49996  * Based on:
49997  * Ext JS Library 1.1.1
49998  * Copyright(c) 2006-2007, Ext JS, LLC.
49999  *
50000  * Originally Released Under LGPL - original licence link has changed is not relivant.
50001  *
50002  * Fork - LGPL
50003  * <script type="text/javascript">
50004  */
50005  
50006  
50007 /*
50008  * Private internal class for reading and applying state
50009  */
50010 Roo.LayoutStateManager = function(layout){
50011      // default empty state
50012      this.state = {
50013         north: {},
50014         south: {},
50015         east: {},
50016         west: {}       
50017     };
50018 };
50019
50020 Roo.LayoutStateManager.prototype = {
50021     init : function(layout, provider){
50022         this.provider = provider;
50023         var state = provider.get(layout.id+"-layout-state");
50024         if(state){
50025             var wasUpdating = layout.isUpdating();
50026             if(!wasUpdating){
50027                 layout.beginUpdate();
50028             }
50029             for(var key in state){
50030                 if(typeof state[key] != "function"){
50031                     var rstate = state[key];
50032                     var r = layout.getRegion(key);
50033                     if(r && rstate){
50034                         if(rstate.size){
50035                             r.resizeTo(rstate.size);
50036                         }
50037                         if(rstate.collapsed == true){
50038                             r.collapse(true);
50039                         }else{
50040                             r.expand(null, true);
50041                         }
50042                     }
50043                 }
50044             }
50045             if(!wasUpdating){
50046                 layout.endUpdate();
50047             }
50048             this.state = state; 
50049         }
50050         this.layout = layout;
50051         layout.on("regionresized", this.onRegionResized, this);
50052         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50053         layout.on("regionexpanded", this.onRegionExpanded, this);
50054     },
50055     
50056     storeState : function(){
50057         this.provider.set(this.layout.id+"-layout-state", this.state);
50058     },
50059     
50060     onRegionResized : function(region, newSize){
50061         this.state[region.getPosition()].size = newSize;
50062         this.storeState();
50063     },
50064     
50065     onRegionCollapsed : function(region){
50066         this.state[region.getPosition()].collapsed = true;
50067         this.storeState();
50068     },
50069     
50070     onRegionExpanded : function(region){
50071         this.state[region.getPosition()].collapsed = false;
50072         this.storeState();
50073     }
50074 };/*
50075  * Based on:
50076  * Ext JS Library 1.1.1
50077  * Copyright(c) 2006-2007, Ext JS, LLC.
50078  *
50079  * Originally Released Under LGPL - original licence link has changed is not relivant.
50080  *
50081  * Fork - LGPL
50082  * <script type="text/javascript">
50083  */
50084 /**
50085  * @class Roo.ContentPanel
50086  * @extends Roo.util.Observable
50087  * A basic ContentPanel element.
50088  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50089  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50090  * @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
50091  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50092  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50093  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50094  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50095  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50096  * @cfg {String} title          The title for this panel
50097  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50098  * @cfg {String} url            Calls {@link #setUrl} with this value
50099  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50100  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50101  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50102  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50103
50104  * @constructor
50105  * Create a new ContentPanel.
50106  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50107  * @param {String/Object} config A string to set only the title or a config object
50108  * @param {String} content (optional) Set the HTML content for this panel
50109  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50110  */
50111 Roo.ContentPanel = function(el, config, content){
50112     
50113      
50114     /*
50115     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50116         config = el;
50117         el = Roo.id();
50118     }
50119     if (config && config.parentLayout) { 
50120         el = config.parentLayout.el.createChild(); 
50121     }
50122     */
50123     if(el.autoCreate){ // xtype is available if this is called from factory
50124         config = el;
50125         el = Roo.id();
50126     }
50127     this.el = Roo.get(el);
50128     if(!this.el && config && config.autoCreate){
50129         if(typeof config.autoCreate == "object"){
50130             if(!config.autoCreate.id){
50131                 config.autoCreate.id = config.id||el;
50132             }
50133             this.el = Roo.DomHelper.append(document.body,
50134                         config.autoCreate, true);
50135         }else{
50136             this.el = Roo.DomHelper.append(document.body,
50137                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50138         }
50139     }
50140     this.closable = false;
50141     this.loaded = false;
50142     this.active = false;
50143     if(typeof config == "string"){
50144         this.title = config;
50145     }else{
50146         Roo.apply(this, config);
50147     }
50148     
50149     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50150         this.wrapEl = this.el.wrap();
50151         this.toolbar.container = this.el.insertSibling(false, 'before');
50152         this.toolbar = new Roo.Toolbar(this.toolbar);
50153     }
50154     
50155     // xtype created footer. - not sure if will work as we normally have to render first..
50156     if (this.footer && !this.footer.el && this.footer.xtype) {
50157         if (!this.wrapEl) {
50158             this.wrapEl = this.el.wrap();
50159         }
50160     
50161         this.footer.container = this.wrapEl.createChild();
50162          
50163         this.footer = Roo.factory(this.footer, Roo);
50164         
50165     }
50166     
50167     if(this.resizeEl){
50168         this.resizeEl = Roo.get(this.resizeEl, true);
50169     }else{
50170         this.resizeEl = this.el;
50171     }
50172     // handle view.xtype
50173     
50174  
50175     
50176     
50177     this.addEvents({
50178         /**
50179          * @event activate
50180          * Fires when this panel is activated. 
50181          * @param {Roo.ContentPanel} this
50182          */
50183         "activate" : true,
50184         /**
50185          * @event deactivate
50186          * Fires when this panel is activated. 
50187          * @param {Roo.ContentPanel} this
50188          */
50189         "deactivate" : true,
50190
50191         /**
50192          * @event resize
50193          * Fires when this panel is resized if fitToFrame is true.
50194          * @param {Roo.ContentPanel} this
50195          * @param {Number} width The width after any component adjustments
50196          * @param {Number} height The height after any component adjustments
50197          */
50198         "resize" : true,
50199         
50200          /**
50201          * @event render
50202          * Fires when this tab is created
50203          * @param {Roo.ContentPanel} this
50204          */
50205         "render" : true
50206         
50207         
50208         
50209     });
50210     
50211
50212     
50213     
50214     if(this.autoScroll){
50215         this.resizeEl.setStyle("overflow", "auto");
50216     } else {
50217         // fix randome scrolling
50218         this.el.on('scroll', function() {
50219             Roo.log('fix random scolling');
50220             this.scrollTo('top',0); 
50221         });
50222     }
50223     content = content || this.content;
50224     if(content){
50225         this.setContent(content);
50226     }
50227     if(config && config.url){
50228         this.setUrl(this.url, this.params, this.loadOnce);
50229     }
50230     
50231     
50232     
50233     Roo.ContentPanel.superclass.constructor.call(this);
50234     
50235     if (this.view && typeof(this.view.xtype) != 'undefined') {
50236         this.view.el = this.el.appendChild(document.createElement("div"));
50237         this.view = Roo.factory(this.view); 
50238         this.view.render  &&  this.view.render(false, '');  
50239     }
50240     
50241     
50242     this.fireEvent('render', this);
50243 };
50244
50245 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50246     tabTip:'',
50247     setRegion : function(region){
50248         this.region = region;
50249         if(region){
50250            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50251         }else{
50252            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50253         } 
50254     },
50255     
50256     /**
50257      * Returns the toolbar for this Panel if one was configured. 
50258      * @return {Roo.Toolbar} 
50259      */
50260     getToolbar : function(){
50261         return this.toolbar;
50262     },
50263     
50264     setActiveState : function(active){
50265         this.active = active;
50266         if(!active){
50267             this.fireEvent("deactivate", this);
50268         }else{
50269             this.fireEvent("activate", this);
50270         }
50271     },
50272     /**
50273      * Updates this panel's element
50274      * @param {String} content The new content
50275      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50276     */
50277     setContent : function(content, loadScripts){
50278         this.el.update(content, loadScripts);
50279     },
50280
50281     ignoreResize : function(w, h){
50282         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50283             return true;
50284         }else{
50285             this.lastSize = {width: w, height: h};
50286             return false;
50287         }
50288     },
50289     /**
50290      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50291      * @return {Roo.UpdateManager} The UpdateManager
50292      */
50293     getUpdateManager : function(){
50294         return this.el.getUpdateManager();
50295     },
50296      /**
50297      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50298      * @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:
50299 <pre><code>
50300 panel.load({
50301     url: "your-url.php",
50302     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50303     callback: yourFunction,
50304     scope: yourObject, //(optional scope)
50305     discardUrl: false,
50306     nocache: false,
50307     text: "Loading...",
50308     timeout: 30,
50309     scripts: false
50310 });
50311 </code></pre>
50312      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50313      * 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.
50314      * @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}
50315      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50316      * @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.
50317      * @return {Roo.ContentPanel} this
50318      */
50319     load : function(){
50320         var um = this.el.getUpdateManager();
50321         um.update.apply(um, arguments);
50322         return this;
50323     },
50324
50325
50326     /**
50327      * 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.
50328      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50329      * @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)
50330      * @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)
50331      * @return {Roo.UpdateManager} The UpdateManager
50332      */
50333     setUrl : function(url, params, loadOnce){
50334         if(this.refreshDelegate){
50335             this.removeListener("activate", this.refreshDelegate);
50336         }
50337         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50338         this.on("activate", this.refreshDelegate);
50339         return this.el.getUpdateManager();
50340     },
50341     
50342     _handleRefresh : function(url, params, loadOnce){
50343         if(!loadOnce || !this.loaded){
50344             var updater = this.el.getUpdateManager();
50345             updater.update(url, params, this._setLoaded.createDelegate(this));
50346         }
50347     },
50348     
50349     _setLoaded : function(){
50350         this.loaded = true;
50351     }, 
50352     
50353     /**
50354      * Returns this panel's id
50355      * @return {String} 
50356      */
50357     getId : function(){
50358         return this.el.id;
50359     },
50360     
50361     /** 
50362      * Returns this panel's element - used by regiosn to add.
50363      * @return {Roo.Element} 
50364      */
50365     getEl : function(){
50366         return this.wrapEl || this.el;
50367     },
50368     
50369     adjustForComponents : function(width, height)
50370     {
50371         //Roo.log('adjustForComponents ');
50372         if(this.resizeEl != this.el){
50373             width -= this.el.getFrameWidth('lr');
50374             height -= this.el.getFrameWidth('tb');
50375         }
50376         if(this.toolbar){
50377             var te = this.toolbar.getEl();
50378             height -= te.getHeight();
50379             te.setWidth(width);
50380         }
50381         if(this.footer){
50382             var te = this.footer.getEl();
50383             Roo.log("footer:" + te.getHeight());
50384             
50385             height -= te.getHeight();
50386             te.setWidth(width);
50387         }
50388         
50389         
50390         if(this.adjustments){
50391             width += this.adjustments[0];
50392             height += this.adjustments[1];
50393         }
50394         return {"width": width, "height": height};
50395     },
50396     
50397     setSize : function(width, height){
50398         if(this.fitToFrame && !this.ignoreResize(width, height)){
50399             if(this.fitContainer && this.resizeEl != this.el){
50400                 this.el.setSize(width, height);
50401             }
50402             var size = this.adjustForComponents(width, height);
50403             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50404             this.fireEvent('resize', this, size.width, size.height);
50405         }
50406     },
50407     
50408     /**
50409      * Returns this panel's title
50410      * @return {String} 
50411      */
50412     getTitle : function(){
50413         return this.title;
50414     },
50415     
50416     /**
50417      * Set this panel's title
50418      * @param {String} title
50419      */
50420     setTitle : function(title){
50421         this.title = title;
50422         if(this.region){
50423             this.region.updatePanelTitle(this, title);
50424         }
50425     },
50426     
50427     /**
50428      * Returns true is this panel was configured to be closable
50429      * @return {Boolean} 
50430      */
50431     isClosable : function(){
50432         return this.closable;
50433     },
50434     
50435     beforeSlide : function(){
50436         this.el.clip();
50437         this.resizeEl.clip();
50438     },
50439     
50440     afterSlide : function(){
50441         this.el.unclip();
50442         this.resizeEl.unclip();
50443     },
50444     
50445     /**
50446      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50447      *   Will fail silently if the {@link #setUrl} method has not been called.
50448      *   This does not activate the panel, just updates its content.
50449      */
50450     refresh : function(){
50451         if(this.refreshDelegate){
50452            this.loaded = false;
50453            this.refreshDelegate();
50454         }
50455     },
50456     
50457     /**
50458      * Destroys this panel
50459      */
50460     destroy : function(){
50461         this.el.removeAllListeners();
50462         var tempEl = document.createElement("span");
50463         tempEl.appendChild(this.el.dom);
50464         tempEl.innerHTML = "";
50465         this.el.remove();
50466         this.el = null;
50467     },
50468     
50469     /**
50470      * form - if the content panel contains a form - this is a reference to it.
50471      * @type {Roo.form.Form}
50472      */
50473     form : false,
50474     /**
50475      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50476      *    This contains a reference to it.
50477      * @type {Roo.View}
50478      */
50479     view : false,
50480     
50481       /**
50482      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50483      * <pre><code>
50484
50485 layout.addxtype({
50486        xtype : 'Form',
50487        items: [ .... ]
50488    }
50489 );
50490
50491 </code></pre>
50492      * @param {Object} cfg Xtype definition of item to add.
50493      */
50494     
50495     addxtype : function(cfg) {
50496         // add form..
50497         if (cfg.xtype.match(/^Form$/)) {
50498             
50499             var el;
50500             //if (this.footer) {
50501             //    el = this.footer.container.insertSibling(false, 'before');
50502             //} else {
50503                 el = this.el.createChild();
50504             //}
50505
50506             this.form = new  Roo.form.Form(cfg);
50507             
50508             
50509             if ( this.form.allItems.length) this.form.render(el.dom);
50510             return this.form;
50511         }
50512         // should only have one of theses..
50513         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50514             // views.. should not be just added - used named prop 'view''
50515             
50516             cfg.el = this.el.appendChild(document.createElement("div"));
50517             // factory?
50518             
50519             var ret = new Roo.factory(cfg);
50520              
50521              ret.render && ret.render(false, ''); // render blank..
50522             this.view = ret;
50523             return ret;
50524         }
50525         return false;
50526     }
50527 });
50528
50529 /**
50530  * @class Roo.GridPanel
50531  * @extends Roo.ContentPanel
50532  * @constructor
50533  * Create a new GridPanel.
50534  * @param {Roo.grid.Grid} grid The grid for this panel
50535  * @param {String/Object} config A string to set only the panel's title, or a config object
50536  */
50537 Roo.GridPanel = function(grid, config){
50538     
50539   
50540     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50541         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50542         
50543     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50544     
50545     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50546     
50547     if(this.toolbar){
50548         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50549     }
50550     // xtype created footer. - not sure if will work as we normally have to render first..
50551     if (this.footer && !this.footer.el && this.footer.xtype) {
50552         
50553         this.footer.container = this.grid.getView().getFooterPanel(true);
50554         this.footer.dataSource = this.grid.dataSource;
50555         this.footer = Roo.factory(this.footer, Roo);
50556         
50557     }
50558     
50559     grid.monitorWindowResize = false; // turn off autosizing
50560     grid.autoHeight = false;
50561     grid.autoWidth = false;
50562     this.grid = grid;
50563     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50564 };
50565
50566 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50567     getId : function(){
50568         return this.grid.id;
50569     },
50570     
50571     /**
50572      * Returns the grid for this panel
50573      * @return {Roo.grid.Grid} 
50574      */
50575     getGrid : function(){
50576         return this.grid;    
50577     },
50578     
50579     setSize : function(width, height){
50580         if(!this.ignoreResize(width, height)){
50581             var grid = this.grid;
50582             var size = this.adjustForComponents(width, height);
50583             grid.getGridEl().setSize(size.width, size.height);
50584             grid.autoSize();
50585         }
50586     },
50587     
50588     beforeSlide : function(){
50589         this.grid.getView().scroller.clip();
50590     },
50591     
50592     afterSlide : function(){
50593         this.grid.getView().scroller.unclip();
50594     },
50595     
50596     destroy : function(){
50597         this.grid.destroy();
50598         delete this.grid;
50599         Roo.GridPanel.superclass.destroy.call(this); 
50600     }
50601 });
50602
50603
50604 /**
50605  * @class Roo.NestedLayoutPanel
50606  * @extends Roo.ContentPanel
50607  * @constructor
50608  * Create a new NestedLayoutPanel.
50609  * 
50610  * 
50611  * @param {Roo.BorderLayout} layout The layout for this panel
50612  * @param {String/Object} config A string to set only the title or a config object
50613  */
50614 Roo.NestedLayoutPanel = function(layout, config)
50615 {
50616     // construct with only one argument..
50617     /* FIXME - implement nicer consturctors
50618     if (layout.layout) {
50619         config = layout;
50620         layout = config.layout;
50621         delete config.layout;
50622     }
50623     if (layout.xtype && !layout.getEl) {
50624         // then layout needs constructing..
50625         layout = Roo.factory(layout, Roo);
50626     }
50627     */
50628     
50629     
50630     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50631     
50632     layout.monitorWindowResize = false; // turn off autosizing
50633     this.layout = layout;
50634     this.layout.getEl().addClass("x-layout-nested-layout");
50635     
50636     
50637     
50638     
50639 };
50640
50641 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50642
50643     setSize : function(width, height){
50644         if(!this.ignoreResize(width, height)){
50645             var size = this.adjustForComponents(width, height);
50646             var el = this.layout.getEl();
50647             el.setSize(size.width, size.height);
50648             var touch = el.dom.offsetWidth;
50649             this.layout.layout();
50650             // ie requires a double layout on the first pass
50651             if(Roo.isIE && !this.initialized){
50652                 this.initialized = true;
50653                 this.layout.layout();
50654             }
50655         }
50656     },
50657     
50658     // activate all subpanels if not currently active..
50659     
50660     setActiveState : function(active){
50661         this.active = active;
50662         if(!active){
50663             this.fireEvent("deactivate", this);
50664             return;
50665         }
50666         
50667         this.fireEvent("activate", this);
50668         // not sure if this should happen before or after..
50669         if (!this.layout) {
50670             return; // should not happen..
50671         }
50672         var reg = false;
50673         for (var r in this.layout.regions) {
50674             reg = this.layout.getRegion(r);
50675             if (reg.getActivePanel()) {
50676                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50677                 reg.setActivePanel(reg.getActivePanel());
50678                 continue;
50679             }
50680             if (!reg.panels.length) {
50681                 continue;
50682             }
50683             reg.showPanel(reg.getPanel(0));
50684         }
50685         
50686         
50687         
50688         
50689     },
50690     
50691     /**
50692      * Returns the nested BorderLayout for this panel
50693      * @return {Roo.BorderLayout} 
50694      */
50695     getLayout : function(){
50696         return this.layout;
50697     },
50698     
50699      /**
50700      * Adds a xtype elements to the layout of the nested panel
50701      * <pre><code>
50702
50703 panel.addxtype({
50704        xtype : 'ContentPanel',
50705        region: 'west',
50706        items: [ .... ]
50707    }
50708 );
50709
50710 panel.addxtype({
50711         xtype : 'NestedLayoutPanel',
50712         region: 'west',
50713         layout: {
50714            center: { },
50715            west: { }   
50716         },
50717         items : [ ... list of content panels or nested layout panels.. ]
50718    }
50719 );
50720 </code></pre>
50721      * @param {Object} cfg Xtype definition of item to add.
50722      */
50723     addxtype : function(cfg) {
50724         return this.layout.addxtype(cfg);
50725     
50726     }
50727 });
50728
50729 Roo.ScrollPanel = function(el, config, content){
50730     config = config || {};
50731     config.fitToFrame = true;
50732     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50733     
50734     this.el.dom.style.overflow = "hidden";
50735     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50736     this.el.removeClass("x-layout-inactive-content");
50737     this.el.on("mousewheel", this.onWheel, this);
50738
50739     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50740     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50741     up.unselectable(); down.unselectable();
50742     up.on("click", this.scrollUp, this);
50743     down.on("click", this.scrollDown, this);
50744     up.addClassOnOver("x-scroller-btn-over");
50745     down.addClassOnOver("x-scroller-btn-over");
50746     up.addClassOnClick("x-scroller-btn-click");
50747     down.addClassOnClick("x-scroller-btn-click");
50748     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50749
50750     this.resizeEl = this.el;
50751     this.el = wrap; this.up = up; this.down = down;
50752 };
50753
50754 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50755     increment : 100,
50756     wheelIncrement : 5,
50757     scrollUp : function(){
50758         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50759     },
50760
50761     scrollDown : function(){
50762         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50763     },
50764
50765     afterScroll : function(){
50766         var el = this.resizeEl;
50767         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50768         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50769         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50770     },
50771
50772     setSize : function(){
50773         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50774         this.afterScroll();
50775     },
50776
50777     onWheel : function(e){
50778         var d = e.getWheelDelta();
50779         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50780         this.afterScroll();
50781         e.stopEvent();
50782     },
50783
50784     setContent : function(content, loadScripts){
50785         this.resizeEl.update(content, loadScripts);
50786     }
50787
50788 });
50789
50790
50791
50792
50793
50794
50795
50796
50797
50798 /**
50799  * @class Roo.TreePanel
50800  * @extends Roo.ContentPanel
50801  * @constructor
50802  * Create a new TreePanel. - defaults to fit/scoll contents.
50803  * @param {String/Object} config A string to set only the panel's title, or a config object
50804  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50805  */
50806 Roo.TreePanel = function(config){
50807     var el = config.el;
50808     var tree = config.tree;
50809     delete config.tree; 
50810     delete config.el; // hopefull!
50811     
50812     // wrapper for IE7 strict & safari scroll issue
50813     
50814     var treeEl = el.createChild();
50815     config.resizeEl = treeEl;
50816     
50817     
50818     
50819     Roo.TreePanel.superclass.constructor.call(this, el, config);
50820  
50821  
50822     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50823     //console.log(tree);
50824     this.on('activate', function()
50825     {
50826         if (this.tree.rendered) {
50827             return;
50828         }
50829         //console.log('render tree');
50830         this.tree.render();
50831     });
50832     // this should not be needed.. - it's actually the 'el' that resizes?
50833     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50834     
50835     //this.on('resize',  function (cp, w, h) {
50836     //        this.tree.innerCt.setWidth(w);
50837     //        this.tree.innerCt.setHeight(h);
50838     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50839     //});
50840
50841         
50842     
50843 };
50844
50845 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50846     fitToFrame : true,
50847     autoScroll : true
50848 });
50849
50850
50851
50852
50853
50854
50855
50856
50857
50858
50859
50860 /*
50861  * Based on:
50862  * Ext JS Library 1.1.1
50863  * Copyright(c) 2006-2007, Ext JS, LLC.
50864  *
50865  * Originally Released Under LGPL - original licence link has changed is not relivant.
50866  *
50867  * Fork - LGPL
50868  * <script type="text/javascript">
50869  */
50870  
50871
50872 /**
50873  * @class Roo.ReaderLayout
50874  * @extends Roo.BorderLayout
50875  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50876  * center region containing two nested regions (a top one for a list view and one for item preview below),
50877  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50878  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50879  * expedites the setup of the overall layout and regions for this common application style.
50880  * Example:
50881  <pre><code>
50882 var reader = new Roo.ReaderLayout();
50883 var CP = Roo.ContentPanel;  // shortcut for adding
50884
50885 reader.beginUpdate();
50886 reader.add("north", new CP("north", "North"));
50887 reader.add("west", new CP("west", {title: "West"}));
50888 reader.add("east", new CP("east", {title: "East"}));
50889
50890 reader.regions.listView.add(new CP("listView", "List"));
50891 reader.regions.preview.add(new CP("preview", "Preview"));
50892 reader.endUpdate();
50893 </code></pre>
50894 * @constructor
50895 * Create a new ReaderLayout
50896 * @param {Object} config Configuration options
50897 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50898 * document.body if omitted)
50899 */
50900 Roo.ReaderLayout = function(config, renderTo){
50901     var c = config || {size:{}};
50902     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50903         north: c.north !== false ? Roo.apply({
50904             split:false,
50905             initialSize: 32,
50906             titlebar: false
50907         }, c.north) : false,
50908         west: c.west !== false ? Roo.apply({
50909             split:true,
50910             initialSize: 200,
50911             minSize: 175,
50912             maxSize: 400,
50913             titlebar: true,
50914             collapsible: true,
50915             animate: true,
50916             margins:{left:5,right:0,bottom:5,top:5},
50917             cmargins:{left:5,right:5,bottom:5,top:5}
50918         }, c.west) : false,
50919         east: c.east !== false ? Roo.apply({
50920             split:true,
50921             initialSize: 200,
50922             minSize: 175,
50923             maxSize: 400,
50924             titlebar: true,
50925             collapsible: true,
50926             animate: true,
50927             margins:{left:0,right:5,bottom:5,top:5},
50928             cmargins:{left:5,right:5,bottom:5,top:5}
50929         }, c.east) : false,
50930         center: Roo.apply({
50931             tabPosition: 'top',
50932             autoScroll:false,
50933             closeOnTab: true,
50934             titlebar:false,
50935             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50936         }, c.center)
50937     });
50938
50939     this.el.addClass('x-reader');
50940
50941     this.beginUpdate();
50942
50943     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50944         south: c.preview !== false ? Roo.apply({
50945             split:true,
50946             initialSize: 200,
50947             minSize: 100,
50948             autoScroll:true,
50949             collapsible:true,
50950             titlebar: true,
50951             cmargins:{top:5,left:0, right:0, bottom:0}
50952         }, c.preview) : false,
50953         center: Roo.apply({
50954             autoScroll:false,
50955             titlebar:false,
50956             minHeight:200
50957         }, c.listView)
50958     });
50959     this.add('center', new Roo.NestedLayoutPanel(inner,
50960             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50961
50962     this.endUpdate();
50963
50964     this.regions.preview = inner.getRegion('south');
50965     this.regions.listView = inner.getRegion('center');
50966 };
50967
50968 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50969  * Based on:
50970  * Ext JS Library 1.1.1
50971  * Copyright(c) 2006-2007, Ext JS, LLC.
50972  *
50973  * Originally Released Under LGPL - original licence link has changed is not relivant.
50974  *
50975  * Fork - LGPL
50976  * <script type="text/javascript">
50977  */
50978  
50979 /**
50980  * @class Roo.grid.Grid
50981  * @extends Roo.util.Observable
50982  * This class represents the primary interface of a component based grid control.
50983  * <br><br>Usage:<pre><code>
50984  var grid = new Roo.grid.Grid("my-container-id", {
50985      ds: myDataStore,
50986      cm: myColModel,
50987      selModel: mySelectionModel,
50988      autoSizeColumns: true,
50989      monitorWindowResize: false,
50990      trackMouseOver: true
50991  });
50992  // set any options
50993  grid.render();
50994  * </code></pre>
50995  * <b>Common Problems:</b><br/>
50996  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
50997  * element will correct this<br/>
50998  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
50999  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51000  * are unpredictable.<br/>
51001  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51002  * grid to calculate dimensions/offsets.<br/>
51003   * @constructor
51004  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51005  * The container MUST have some type of size defined for the grid to fill. The container will be
51006  * automatically set to position relative if it isn't already.
51007  * @param {Object} config A config object that sets properties on this grid.
51008  */
51009 Roo.grid.Grid = function(container, config){
51010         // initialize the container
51011         this.container = Roo.get(container);
51012         this.container.update("");
51013         this.container.setStyle("overflow", "hidden");
51014     this.container.addClass('x-grid-container');
51015
51016     this.id = this.container.id;
51017
51018     Roo.apply(this, config);
51019     // check and correct shorthanded configs
51020     if(this.ds){
51021         this.dataSource = this.ds;
51022         delete this.ds;
51023     }
51024     if(this.cm){
51025         this.colModel = this.cm;
51026         delete this.cm;
51027     }
51028     if(this.sm){
51029         this.selModel = this.sm;
51030         delete this.sm;
51031     }
51032
51033     if (this.selModel) {
51034         this.selModel = Roo.factory(this.selModel, Roo.grid);
51035         this.sm = this.selModel;
51036         this.sm.xmodule = this.xmodule || false;
51037     }
51038     if (typeof(this.colModel.config) == 'undefined') {
51039         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51040         this.cm = this.colModel;
51041         this.cm.xmodule = this.xmodule || false;
51042     }
51043     if (this.dataSource) {
51044         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51045         this.ds = this.dataSource;
51046         this.ds.xmodule = this.xmodule || false;
51047          
51048     }
51049     
51050     
51051     
51052     if(this.width){
51053         this.container.setWidth(this.width);
51054     }
51055
51056     if(this.height){
51057         this.container.setHeight(this.height);
51058     }
51059     /** @private */
51060         this.addEvents({
51061         // raw events
51062         /**
51063          * @event click
51064          * The raw click event for the entire grid.
51065          * @param {Roo.EventObject} e
51066          */
51067         "click" : true,
51068         /**
51069          * @event dblclick
51070          * The raw dblclick event for the entire grid.
51071          * @param {Roo.EventObject} e
51072          */
51073         "dblclick" : true,
51074         /**
51075          * @event contextmenu
51076          * The raw contextmenu event for the entire grid.
51077          * @param {Roo.EventObject} e
51078          */
51079         "contextmenu" : true,
51080         /**
51081          * @event mousedown
51082          * The raw mousedown event for the entire grid.
51083          * @param {Roo.EventObject} e
51084          */
51085         "mousedown" : true,
51086         /**
51087          * @event mouseup
51088          * The raw mouseup event for the entire grid.
51089          * @param {Roo.EventObject} e
51090          */
51091         "mouseup" : true,
51092         /**
51093          * @event mouseover
51094          * The raw mouseover event for the entire grid.
51095          * @param {Roo.EventObject} e
51096          */
51097         "mouseover" : true,
51098         /**
51099          * @event mouseout
51100          * The raw mouseout event for the entire grid.
51101          * @param {Roo.EventObject} e
51102          */
51103         "mouseout" : true,
51104         /**
51105          * @event keypress
51106          * The raw keypress event for the entire grid.
51107          * @param {Roo.EventObject} e
51108          */
51109         "keypress" : true,
51110         /**
51111          * @event keydown
51112          * The raw keydown event for the entire grid.
51113          * @param {Roo.EventObject} e
51114          */
51115         "keydown" : true,
51116
51117         // custom events
51118
51119         /**
51120          * @event cellclick
51121          * Fires when a cell is clicked
51122          * @param {Grid} this
51123          * @param {Number} rowIndex
51124          * @param {Number} columnIndex
51125          * @param {Roo.EventObject} e
51126          */
51127         "cellclick" : true,
51128         /**
51129          * @event celldblclick
51130          * Fires when a cell is double clicked
51131          * @param {Grid} this
51132          * @param {Number} rowIndex
51133          * @param {Number} columnIndex
51134          * @param {Roo.EventObject} e
51135          */
51136         "celldblclick" : true,
51137         /**
51138          * @event rowclick
51139          * Fires when a row is clicked
51140          * @param {Grid} this
51141          * @param {Number} rowIndex
51142          * @param {Roo.EventObject} e
51143          */
51144         "rowclick" : true,
51145         /**
51146          * @event rowdblclick
51147          * Fires when a row is double clicked
51148          * @param {Grid} this
51149          * @param {Number} rowIndex
51150          * @param {Roo.EventObject} e
51151          */
51152         "rowdblclick" : true,
51153         /**
51154          * @event headerclick
51155          * Fires when a header is clicked
51156          * @param {Grid} this
51157          * @param {Number} columnIndex
51158          * @param {Roo.EventObject} e
51159          */
51160         "headerclick" : true,
51161         /**
51162          * @event headerdblclick
51163          * Fires when a header cell is double clicked
51164          * @param {Grid} this
51165          * @param {Number} columnIndex
51166          * @param {Roo.EventObject} e
51167          */
51168         "headerdblclick" : true,
51169         /**
51170          * @event rowcontextmenu
51171          * Fires when a row is right clicked
51172          * @param {Grid} this
51173          * @param {Number} rowIndex
51174          * @param {Roo.EventObject} e
51175          */
51176         "rowcontextmenu" : true,
51177         /**
51178          * @event cellcontextmenu
51179          * Fires when a cell is right clicked
51180          * @param {Grid} this
51181          * @param {Number} rowIndex
51182          * @param {Number} cellIndex
51183          * @param {Roo.EventObject} e
51184          */
51185          "cellcontextmenu" : true,
51186         /**
51187          * @event headercontextmenu
51188          * Fires when a header is right clicked
51189          * @param {Grid} this
51190          * @param {Number} columnIndex
51191          * @param {Roo.EventObject} e
51192          */
51193         "headercontextmenu" : true,
51194         /**
51195          * @event bodyscroll
51196          * Fires when the body element is scrolled
51197          * @param {Number} scrollLeft
51198          * @param {Number} scrollTop
51199          */
51200         "bodyscroll" : true,
51201         /**
51202          * @event columnresize
51203          * Fires when the user resizes a column
51204          * @param {Number} columnIndex
51205          * @param {Number} newSize
51206          */
51207         "columnresize" : true,
51208         /**
51209          * @event columnmove
51210          * Fires when the user moves a column
51211          * @param {Number} oldIndex
51212          * @param {Number} newIndex
51213          */
51214         "columnmove" : true,
51215         /**
51216          * @event startdrag
51217          * Fires when row(s) start being dragged
51218          * @param {Grid} this
51219          * @param {Roo.GridDD} dd The drag drop object
51220          * @param {event} e The raw browser event
51221          */
51222         "startdrag" : true,
51223         /**
51224          * @event enddrag
51225          * Fires when a drag operation is complete
51226          * @param {Grid} this
51227          * @param {Roo.GridDD} dd The drag drop object
51228          * @param {event} e The raw browser event
51229          */
51230         "enddrag" : true,
51231         /**
51232          * @event dragdrop
51233          * Fires when dragged row(s) are dropped on a valid DD target
51234          * @param {Grid} this
51235          * @param {Roo.GridDD} dd The drag drop object
51236          * @param {String} targetId The target drag drop object
51237          * @param {event} e The raw browser event
51238          */
51239         "dragdrop" : true,
51240         /**
51241          * @event dragover
51242          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51243          * @param {Grid} this
51244          * @param {Roo.GridDD} dd The drag drop object
51245          * @param {String} targetId The target drag drop object
51246          * @param {event} e The raw browser event
51247          */
51248         "dragover" : true,
51249         /**
51250          * @event dragenter
51251          *  Fires when the dragged row(s) first cross another DD target while being dragged
51252          * @param {Grid} this
51253          * @param {Roo.GridDD} dd The drag drop object
51254          * @param {String} targetId The target drag drop object
51255          * @param {event} e The raw browser event
51256          */
51257         "dragenter" : true,
51258         /**
51259          * @event dragout
51260          * Fires when the dragged row(s) leave another DD target while being dragged
51261          * @param {Grid} this
51262          * @param {Roo.GridDD} dd The drag drop object
51263          * @param {String} targetId The target drag drop object
51264          * @param {event} e The raw browser event
51265          */
51266         "dragout" : true,
51267         /**
51268          * @event rowclass
51269          * Fires when a row is rendered, so you can change add a style to it.
51270          * @param {GridView} gridview   The grid view
51271          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51272          */
51273         'rowclass' : true,
51274
51275         /**
51276          * @event render
51277          * Fires when the grid is rendered
51278          * @param {Grid} grid
51279          */
51280         'render' : true
51281     });
51282
51283     Roo.grid.Grid.superclass.constructor.call(this);
51284 };
51285 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51286     
51287     /**
51288      * @cfg {String} ddGroup - drag drop group.
51289      */
51290
51291     /**
51292      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51293      */
51294     minColumnWidth : 25,
51295
51296     /**
51297      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51298      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51299      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51300      */
51301     autoSizeColumns : false,
51302
51303     /**
51304      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51305      */
51306     autoSizeHeaders : true,
51307
51308     /**
51309      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51310      */
51311     monitorWindowResize : true,
51312
51313     /**
51314      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51315      * rows measured to get a columns size. Default is 0 (all rows).
51316      */
51317     maxRowsToMeasure : 0,
51318
51319     /**
51320      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51321      */
51322     trackMouseOver : true,
51323
51324     /**
51325     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51326     */
51327     
51328     /**
51329     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51330     */
51331     enableDragDrop : false,
51332     
51333     /**
51334     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51335     */
51336     enableColumnMove : true,
51337     
51338     /**
51339     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51340     */
51341     enableColumnHide : true,
51342     
51343     /**
51344     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51345     */
51346     enableRowHeightSync : false,
51347     
51348     /**
51349     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51350     */
51351     stripeRows : true,
51352     
51353     /**
51354     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51355     */
51356     autoHeight : false,
51357
51358     /**
51359      * @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.
51360      */
51361     autoExpandColumn : false,
51362
51363     /**
51364     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51365     * Default is 50.
51366     */
51367     autoExpandMin : 50,
51368
51369     /**
51370     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51371     */
51372     autoExpandMax : 1000,
51373
51374     /**
51375     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51376     */
51377     view : null,
51378
51379     /**
51380     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51381     */
51382     loadMask : false,
51383     /**
51384     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51385     */
51386     dropTarget: false,
51387     
51388    
51389     
51390     // private
51391     rendered : false,
51392
51393     /**
51394     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51395     * of a fixed width. Default is false.
51396     */
51397     /**
51398     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51399     */
51400     /**
51401      * Called once after all setup has been completed and the grid is ready to be rendered.
51402      * @return {Roo.grid.Grid} this
51403      */
51404     render : function()
51405     {
51406         var c = this.container;
51407         // try to detect autoHeight/width mode
51408         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51409             this.autoHeight = true;
51410         }
51411         var view = this.getView();
51412         view.init(this);
51413
51414         c.on("click", this.onClick, this);
51415         c.on("dblclick", this.onDblClick, this);
51416         c.on("contextmenu", this.onContextMenu, this);
51417         c.on("keydown", this.onKeyDown, this);
51418         if (Roo.isTouch) {
51419             c.on("touchstart", this.onTouchStart, this);
51420         }
51421
51422         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51423
51424         this.getSelectionModel().init(this);
51425
51426         view.render();
51427
51428         if(this.loadMask){
51429             this.loadMask = new Roo.LoadMask(this.container,
51430                     Roo.apply({store:this.dataSource}, this.loadMask));
51431         }
51432         
51433         
51434         if (this.toolbar && this.toolbar.xtype) {
51435             this.toolbar.container = this.getView().getHeaderPanel(true);
51436             this.toolbar = new Roo.Toolbar(this.toolbar);
51437         }
51438         if (this.footer && this.footer.xtype) {
51439             this.footer.dataSource = this.getDataSource();
51440             this.footer.container = this.getView().getFooterPanel(true);
51441             this.footer = Roo.factory(this.footer, Roo);
51442         }
51443         if (this.dropTarget && this.dropTarget.xtype) {
51444             delete this.dropTarget.xtype;
51445             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51446         }
51447         
51448         
51449         this.rendered = true;
51450         this.fireEvent('render', this);
51451         return this;
51452     },
51453
51454         /**
51455          * Reconfigures the grid to use a different Store and Column Model.
51456          * The View will be bound to the new objects and refreshed.
51457          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51458          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51459          */
51460     reconfigure : function(dataSource, colModel){
51461         if(this.loadMask){
51462             this.loadMask.destroy();
51463             this.loadMask = new Roo.LoadMask(this.container,
51464                     Roo.apply({store:dataSource}, this.loadMask));
51465         }
51466         this.view.bind(dataSource, colModel);
51467         this.dataSource = dataSource;
51468         this.colModel = colModel;
51469         this.view.refresh(true);
51470     },
51471
51472     // private
51473     onKeyDown : function(e){
51474         this.fireEvent("keydown", e);
51475     },
51476
51477     /**
51478      * Destroy this grid.
51479      * @param {Boolean} removeEl True to remove the element
51480      */
51481     destroy : function(removeEl, keepListeners){
51482         if(this.loadMask){
51483             this.loadMask.destroy();
51484         }
51485         var c = this.container;
51486         c.removeAllListeners();
51487         this.view.destroy();
51488         this.colModel.purgeListeners();
51489         if(!keepListeners){
51490             this.purgeListeners();
51491         }
51492         c.update("");
51493         if(removeEl === true){
51494             c.remove();
51495         }
51496     },
51497
51498     // private
51499     processEvent : function(name, e){
51500         // does this fire select???
51501         Roo.log('grid:processEvent '  + name);
51502         
51503         if (name != 'touchstart' ) {
51504             this.fireEvent(name, e);    
51505         }
51506         
51507         var t = e.getTarget();
51508         var v = this.view;
51509         var header = v.findHeaderIndex(t);
51510         if(header !== false){
51511             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51512         }else{
51513             var row = v.findRowIndex(t);
51514             var cell = v.findCellIndex(t);
51515             if (name == 'touchstart') {
51516                 // first touch is always a click.
51517                 // hopefull this happens after selection is updated.?
51518                 name = false;
51519                 
51520                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51521                     var cs = this.selModel.getSelectedCell();
51522                     if (row == cs[0] && cell == cs[1]){
51523                         name = 'dblclick';
51524                     }
51525                 }
51526                 if (typeof(this.selModel.getSelections) != 'undefined') {
51527                     var cs = this.selModel.getSelections();
51528                     var ds = this.dataSource;
51529                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51530                         name = 'dblclick';
51531                     }
51532                 }
51533                 if (!name) {
51534                     return;
51535                 }
51536             }
51537             
51538             
51539             if(row !== false){
51540                 this.fireEvent("row" + name, this, row, e);
51541                 if(cell !== false){
51542                     this.fireEvent("cell" + name, this, row, cell, e);
51543                 }
51544             }
51545         }
51546     },
51547
51548     // private
51549     onClick : function(e){
51550         this.processEvent("click", e);
51551     },
51552    // private
51553     onTouchStart : function(e){
51554         this.processEvent("touchstart", e);
51555     },
51556
51557     // private
51558     onContextMenu : function(e, t){
51559         this.processEvent("contextmenu", e);
51560     },
51561
51562     // private
51563     onDblClick : function(e){
51564         this.processEvent("dblclick", e);
51565     },
51566
51567     // private
51568     walkCells : function(row, col, step, fn, scope){
51569         var cm = this.colModel, clen = cm.getColumnCount();
51570         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51571         if(step < 0){
51572             if(col < 0){
51573                 row--;
51574                 first = false;
51575             }
51576             while(row >= 0){
51577                 if(!first){
51578                     col = clen-1;
51579                 }
51580                 first = false;
51581                 while(col >= 0){
51582                     if(fn.call(scope || this, row, col, cm) === true){
51583                         return [row, col];
51584                     }
51585                     col--;
51586                 }
51587                 row--;
51588             }
51589         } else {
51590             if(col >= clen){
51591                 row++;
51592                 first = false;
51593             }
51594             while(row < rlen){
51595                 if(!first){
51596                     col = 0;
51597                 }
51598                 first = false;
51599                 while(col < clen){
51600                     if(fn.call(scope || this, row, col, cm) === true){
51601                         return [row, col];
51602                     }
51603                     col++;
51604                 }
51605                 row++;
51606             }
51607         }
51608         return null;
51609     },
51610
51611     // private
51612     getSelections : function(){
51613         return this.selModel.getSelections();
51614     },
51615
51616     /**
51617      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51618      * but if manual update is required this method will initiate it.
51619      */
51620     autoSize : function(){
51621         if(this.rendered){
51622             this.view.layout();
51623             if(this.view.adjustForScroll){
51624                 this.view.adjustForScroll();
51625             }
51626         }
51627     },
51628
51629     /**
51630      * Returns the grid's underlying element.
51631      * @return {Element} The element
51632      */
51633     getGridEl : function(){
51634         return this.container;
51635     },
51636
51637     // private for compatibility, overridden by editor grid
51638     stopEditing : function(){},
51639
51640     /**
51641      * Returns the grid's SelectionModel.
51642      * @return {SelectionModel}
51643      */
51644     getSelectionModel : function(){
51645         if(!this.selModel){
51646             this.selModel = new Roo.grid.RowSelectionModel();
51647         }
51648         return this.selModel;
51649     },
51650
51651     /**
51652      * Returns the grid's DataSource.
51653      * @return {DataSource}
51654      */
51655     getDataSource : function(){
51656         return this.dataSource;
51657     },
51658
51659     /**
51660      * Returns the grid's ColumnModel.
51661      * @return {ColumnModel}
51662      */
51663     getColumnModel : function(){
51664         return this.colModel;
51665     },
51666
51667     /**
51668      * Returns the grid's GridView object.
51669      * @return {GridView}
51670      */
51671     getView : function(){
51672         if(!this.view){
51673             this.view = new Roo.grid.GridView(this.viewConfig);
51674         }
51675         return this.view;
51676     },
51677     /**
51678      * Called to get grid's drag proxy text, by default returns this.ddText.
51679      * @return {String}
51680      */
51681     getDragDropText : function(){
51682         var count = this.selModel.getCount();
51683         return String.format(this.ddText, count, count == 1 ? '' : 's');
51684     }
51685 });
51686 /**
51687  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51688  * %0 is replaced with the number of selected rows.
51689  * @type String
51690  */
51691 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51692  * Based on:
51693  * Ext JS Library 1.1.1
51694  * Copyright(c) 2006-2007, Ext JS, LLC.
51695  *
51696  * Originally Released Under LGPL - original licence link has changed is not relivant.
51697  *
51698  * Fork - LGPL
51699  * <script type="text/javascript">
51700  */
51701  
51702 Roo.grid.AbstractGridView = function(){
51703         this.grid = null;
51704         
51705         this.events = {
51706             "beforerowremoved" : true,
51707             "beforerowsinserted" : true,
51708             "beforerefresh" : true,
51709             "rowremoved" : true,
51710             "rowsinserted" : true,
51711             "rowupdated" : true,
51712             "refresh" : true
51713         };
51714     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51715 };
51716
51717 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51718     rowClass : "x-grid-row",
51719     cellClass : "x-grid-cell",
51720     tdClass : "x-grid-td",
51721     hdClass : "x-grid-hd",
51722     splitClass : "x-grid-hd-split",
51723     
51724         init: function(grid){
51725         this.grid = grid;
51726                 var cid = this.grid.getGridEl().id;
51727         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51728         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51729         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51730         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51731         },
51732         
51733         getColumnRenderers : function(){
51734         var renderers = [];
51735         var cm = this.grid.colModel;
51736         var colCount = cm.getColumnCount();
51737         for(var i = 0; i < colCount; i++){
51738             renderers[i] = cm.getRenderer(i);
51739         }
51740         return renderers;
51741     },
51742     
51743     getColumnIds : function(){
51744         var ids = [];
51745         var cm = this.grid.colModel;
51746         var colCount = cm.getColumnCount();
51747         for(var i = 0; i < colCount; i++){
51748             ids[i] = cm.getColumnId(i);
51749         }
51750         return ids;
51751     },
51752     
51753     getDataIndexes : function(){
51754         if(!this.indexMap){
51755             this.indexMap = this.buildIndexMap();
51756         }
51757         return this.indexMap.colToData;
51758     },
51759     
51760     getColumnIndexByDataIndex : function(dataIndex){
51761         if(!this.indexMap){
51762             this.indexMap = this.buildIndexMap();
51763         }
51764         return this.indexMap.dataToCol[dataIndex];
51765     },
51766     
51767     /**
51768      * Set a css style for a column dynamically. 
51769      * @param {Number} colIndex The index of the column
51770      * @param {String} name The css property name
51771      * @param {String} value The css value
51772      */
51773     setCSSStyle : function(colIndex, name, value){
51774         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51775         Roo.util.CSS.updateRule(selector, name, value);
51776     },
51777     
51778     generateRules : function(cm){
51779         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51780         Roo.util.CSS.removeStyleSheet(rulesId);
51781         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51782             var cid = cm.getColumnId(i);
51783             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51784                          this.tdSelector, cid, " {\n}\n",
51785                          this.hdSelector, cid, " {\n}\n",
51786                          this.splitSelector, cid, " {\n}\n");
51787         }
51788         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51789     }
51790 });/*
51791  * Based on:
51792  * Ext JS Library 1.1.1
51793  * Copyright(c) 2006-2007, Ext JS, LLC.
51794  *
51795  * Originally Released Under LGPL - original licence link has changed is not relivant.
51796  *
51797  * Fork - LGPL
51798  * <script type="text/javascript">
51799  */
51800
51801 // private
51802 // This is a support class used internally by the Grid components
51803 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51804     this.grid = grid;
51805     this.view = grid.getView();
51806     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51807     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51808     if(hd2){
51809         this.setHandleElId(Roo.id(hd));
51810         this.setOuterHandleElId(Roo.id(hd2));
51811     }
51812     this.scroll = false;
51813 };
51814 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51815     maxDragWidth: 120,
51816     getDragData : function(e){
51817         var t = Roo.lib.Event.getTarget(e);
51818         var h = this.view.findHeaderCell(t);
51819         if(h){
51820             return {ddel: h.firstChild, header:h};
51821         }
51822         return false;
51823     },
51824
51825     onInitDrag : function(e){
51826         this.view.headersDisabled = true;
51827         var clone = this.dragData.ddel.cloneNode(true);
51828         clone.id = Roo.id();
51829         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51830         this.proxy.update(clone);
51831         return true;
51832     },
51833
51834     afterValidDrop : function(){
51835         var v = this.view;
51836         setTimeout(function(){
51837             v.headersDisabled = false;
51838         }, 50);
51839     },
51840
51841     afterInvalidDrop : function(){
51842         var v = this.view;
51843         setTimeout(function(){
51844             v.headersDisabled = false;
51845         }, 50);
51846     }
51847 });
51848 /*
51849  * Based on:
51850  * Ext JS Library 1.1.1
51851  * Copyright(c) 2006-2007, Ext JS, LLC.
51852  *
51853  * Originally Released Under LGPL - original licence link has changed is not relivant.
51854  *
51855  * Fork - LGPL
51856  * <script type="text/javascript">
51857  */
51858 // private
51859 // This is a support class used internally by the Grid components
51860 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51861     this.grid = grid;
51862     this.view = grid.getView();
51863     // split the proxies so they don't interfere with mouse events
51864     this.proxyTop = Roo.DomHelper.append(document.body, {
51865         cls:"col-move-top", html:"&#160;"
51866     }, true);
51867     this.proxyBottom = Roo.DomHelper.append(document.body, {
51868         cls:"col-move-bottom", html:"&#160;"
51869     }, true);
51870     this.proxyTop.hide = this.proxyBottom.hide = function(){
51871         this.setLeftTop(-100,-100);
51872         this.setStyle("visibility", "hidden");
51873     };
51874     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51875     // temporarily disabled
51876     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51877     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51878 };
51879 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51880     proxyOffsets : [-4, -9],
51881     fly: Roo.Element.fly,
51882
51883     getTargetFromEvent : function(e){
51884         var t = Roo.lib.Event.getTarget(e);
51885         var cindex = this.view.findCellIndex(t);
51886         if(cindex !== false){
51887             return this.view.getHeaderCell(cindex);
51888         }
51889         return null;
51890     },
51891
51892     nextVisible : function(h){
51893         var v = this.view, cm = this.grid.colModel;
51894         h = h.nextSibling;
51895         while(h){
51896             if(!cm.isHidden(v.getCellIndex(h))){
51897                 return h;
51898             }
51899             h = h.nextSibling;
51900         }
51901         return null;
51902     },
51903
51904     prevVisible : function(h){
51905         var v = this.view, cm = this.grid.colModel;
51906         h = h.prevSibling;
51907         while(h){
51908             if(!cm.isHidden(v.getCellIndex(h))){
51909                 return h;
51910             }
51911             h = h.prevSibling;
51912         }
51913         return null;
51914     },
51915
51916     positionIndicator : function(h, n, e){
51917         var x = Roo.lib.Event.getPageX(e);
51918         var r = Roo.lib.Dom.getRegion(n.firstChild);
51919         var px, pt, py = r.top + this.proxyOffsets[1];
51920         if((r.right - x) <= (r.right-r.left)/2){
51921             px = r.right+this.view.borderWidth;
51922             pt = "after";
51923         }else{
51924             px = r.left;
51925             pt = "before";
51926         }
51927         var oldIndex = this.view.getCellIndex(h);
51928         var newIndex = this.view.getCellIndex(n);
51929
51930         if(this.grid.colModel.isFixed(newIndex)){
51931             return false;
51932         }
51933
51934         var locked = this.grid.colModel.isLocked(newIndex);
51935
51936         if(pt == "after"){
51937             newIndex++;
51938         }
51939         if(oldIndex < newIndex){
51940             newIndex--;
51941         }
51942         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51943             return false;
51944         }
51945         px +=  this.proxyOffsets[0];
51946         this.proxyTop.setLeftTop(px, py);
51947         this.proxyTop.show();
51948         if(!this.bottomOffset){
51949             this.bottomOffset = this.view.mainHd.getHeight();
51950         }
51951         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51952         this.proxyBottom.show();
51953         return pt;
51954     },
51955
51956     onNodeEnter : function(n, dd, e, data){
51957         if(data.header != n){
51958             this.positionIndicator(data.header, n, e);
51959         }
51960     },
51961
51962     onNodeOver : function(n, dd, e, data){
51963         var result = false;
51964         if(data.header != n){
51965             result = this.positionIndicator(data.header, n, e);
51966         }
51967         if(!result){
51968             this.proxyTop.hide();
51969             this.proxyBottom.hide();
51970         }
51971         return result ? this.dropAllowed : this.dropNotAllowed;
51972     },
51973
51974     onNodeOut : function(n, dd, e, data){
51975         this.proxyTop.hide();
51976         this.proxyBottom.hide();
51977     },
51978
51979     onNodeDrop : function(n, dd, e, data){
51980         var h = data.header;
51981         if(h != n){
51982             var cm = this.grid.colModel;
51983             var x = Roo.lib.Event.getPageX(e);
51984             var r = Roo.lib.Dom.getRegion(n.firstChild);
51985             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
51986             var oldIndex = this.view.getCellIndex(h);
51987             var newIndex = this.view.getCellIndex(n);
51988             var locked = cm.isLocked(newIndex);
51989             if(pt == "after"){
51990                 newIndex++;
51991             }
51992             if(oldIndex < newIndex){
51993                 newIndex--;
51994             }
51995             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
51996                 return false;
51997             }
51998             cm.setLocked(oldIndex, locked, true);
51999             cm.moveColumn(oldIndex, newIndex);
52000             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52001             return true;
52002         }
52003         return false;
52004     }
52005 });
52006 /*
52007  * Based on:
52008  * Ext JS Library 1.1.1
52009  * Copyright(c) 2006-2007, Ext JS, LLC.
52010  *
52011  * Originally Released Under LGPL - original licence link has changed is not relivant.
52012  *
52013  * Fork - LGPL
52014  * <script type="text/javascript">
52015  */
52016   
52017 /**
52018  * @class Roo.grid.GridView
52019  * @extends Roo.util.Observable
52020  *
52021  * @constructor
52022  * @param {Object} config
52023  */
52024 Roo.grid.GridView = function(config){
52025     Roo.grid.GridView.superclass.constructor.call(this);
52026     this.el = null;
52027
52028     Roo.apply(this, config);
52029 };
52030
52031 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52032
52033     unselectable :  'unselectable="on"',
52034     unselectableCls :  'x-unselectable',
52035     
52036     
52037     rowClass : "x-grid-row",
52038
52039     cellClass : "x-grid-col",
52040
52041     tdClass : "x-grid-td",
52042
52043     hdClass : "x-grid-hd",
52044
52045     splitClass : "x-grid-split",
52046
52047     sortClasses : ["sort-asc", "sort-desc"],
52048
52049     enableMoveAnim : false,
52050
52051     hlColor: "C3DAF9",
52052
52053     dh : Roo.DomHelper,
52054
52055     fly : Roo.Element.fly,
52056
52057     css : Roo.util.CSS,
52058
52059     borderWidth: 1,
52060
52061     splitOffset: 3,
52062
52063     scrollIncrement : 22,
52064
52065     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52066
52067     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52068
52069     bind : function(ds, cm){
52070         if(this.ds){
52071             this.ds.un("load", this.onLoad, this);
52072             this.ds.un("datachanged", this.onDataChange, this);
52073             this.ds.un("add", this.onAdd, this);
52074             this.ds.un("remove", this.onRemove, this);
52075             this.ds.un("update", this.onUpdate, this);
52076             this.ds.un("clear", this.onClear, this);
52077         }
52078         if(ds){
52079             ds.on("load", this.onLoad, this);
52080             ds.on("datachanged", this.onDataChange, this);
52081             ds.on("add", this.onAdd, this);
52082             ds.on("remove", this.onRemove, this);
52083             ds.on("update", this.onUpdate, this);
52084             ds.on("clear", this.onClear, this);
52085         }
52086         this.ds = ds;
52087
52088         if(this.cm){
52089             this.cm.un("widthchange", this.onColWidthChange, this);
52090             this.cm.un("headerchange", this.onHeaderChange, this);
52091             this.cm.un("hiddenchange", this.onHiddenChange, this);
52092             this.cm.un("columnmoved", this.onColumnMove, this);
52093             this.cm.un("columnlockchange", this.onColumnLock, this);
52094         }
52095         if(cm){
52096             this.generateRules(cm);
52097             cm.on("widthchange", this.onColWidthChange, this);
52098             cm.on("headerchange", this.onHeaderChange, this);
52099             cm.on("hiddenchange", this.onHiddenChange, this);
52100             cm.on("columnmoved", this.onColumnMove, this);
52101             cm.on("columnlockchange", this.onColumnLock, this);
52102         }
52103         this.cm = cm;
52104     },
52105
52106     init: function(grid){
52107         Roo.grid.GridView.superclass.init.call(this, grid);
52108
52109         this.bind(grid.dataSource, grid.colModel);
52110
52111         grid.on("headerclick", this.handleHeaderClick, this);
52112
52113         if(grid.trackMouseOver){
52114             grid.on("mouseover", this.onRowOver, this);
52115             grid.on("mouseout", this.onRowOut, this);
52116         }
52117         grid.cancelTextSelection = function(){};
52118         this.gridId = grid.id;
52119
52120         var tpls = this.templates || {};
52121
52122         if(!tpls.master){
52123             tpls.master = new Roo.Template(
52124                '<div class="x-grid" hidefocus="true">',
52125                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52126                   '<div class="x-grid-topbar"></div>',
52127                   '<div class="x-grid-scroller"><div></div></div>',
52128                   '<div class="x-grid-locked">',
52129                       '<div class="x-grid-header">{lockedHeader}</div>',
52130                       '<div class="x-grid-body">{lockedBody}</div>',
52131                   "</div>",
52132                   '<div class="x-grid-viewport">',
52133                       '<div class="x-grid-header">{header}</div>',
52134                       '<div class="x-grid-body">{body}</div>',
52135                   "</div>",
52136                   '<div class="x-grid-bottombar"></div>',
52137                  
52138                   '<div class="x-grid-resize-proxy">&#160;</div>',
52139                "</div>"
52140             );
52141             tpls.master.disableformats = true;
52142         }
52143
52144         if(!tpls.header){
52145             tpls.header = new Roo.Template(
52146                '<table border="0" cellspacing="0" cellpadding="0">',
52147                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52148                "</table>{splits}"
52149             );
52150             tpls.header.disableformats = true;
52151         }
52152         tpls.header.compile();
52153
52154         if(!tpls.hcell){
52155             tpls.hcell = new Roo.Template(
52156                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52157                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52158                 "</div></td>"
52159              );
52160              tpls.hcell.disableFormats = true;
52161         }
52162         tpls.hcell.compile();
52163
52164         if(!tpls.hsplit){
52165             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52166                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52167             tpls.hsplit.disableFormats = true;
52168         }
52169         tpls.hsplit.compile();
52170
52171         if(!tpls.body){
52172             tpls.body = new Roo.Template(
52173                '<table border="0" cellspacing="0" cellpadding="0">',
52174                "<tbody>{rows}</tbody>",
52175                "</table>"
52176             );
52177             tpls.body.disableFormats = true;
52178         }
52179         tpls.body.compile();
52180
52181         if(!tpls.row){
52182             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52183             tpls.row.disableFormats = true;
52184         }
52185         tpls.row.compile();
52186
52187         if(!tpls.cell){
52188             tpls.cell = new Roo.Template(
52189                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52190                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52191                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52192                 "</td>"
52193             );
52194             tpls.cell.disableFormats = true;
52195         }
52196         tpls.cell.compile();
52197
52198         this.templates = tpls;
52199     },
52200
52201     // remap these for backwards compat
52202     onColWidthChange : function(){
52203         this.updateColumns.apply(this, arguments);
52204     },
52205     onHeaderChange : function(){
52206         this.updateHeaders.apply(this, arguments);
52207     }, 
52208     onHiddenChange : function(){
52209         this.handleHiddenChange.apply(this, arguments);
52210     },
52211     onColumnMove : function(){
52212         this.handleColumnMove.apply(this, arguments);
52213     },
52214     onColumnLock : function(){
52215         this.handleLockChange.apply(this, arguments);
52216     },
52217
52218     onDataChange : function(){
52219         this.refresh();
52220         this.updateHeaderSortState();
52221     },
52222
52223     onClear : function(){
52224         this.refresh();
52225     },
52226
52227     onUpdate : function(ds, record){
52228         this.refreshRow(record);
52229     },
52230
52231     refreshRow : function(record){
52232         var ds = this.ds, index;
52233         if(typeof record == 'number'){
52234             index = record;
52235             record = ds.getAt(index);
52236         }else{
52237             index = ds.indexOf(record);
52238         }
52239         this.insertRows(ds, index, index, true);
52240         this.onRemove(ds, record, index+1, true);
52241         this.syncRowHeights(index, index);
52242         this.layout();
52243         this.fireEvent("rowupdated", this, index, record);
52244     },
52245
52246     onAdd : function(ds, records, index){
52247         this.insertRows(ds, index, index + (records.length-1));
52248     },
52249
52250     onRemove : function(ds, record, index, isUpdate){
52251         if(isUpdate !== true){
52252             this.fireEvent("beforerowremoved", this, index, record);
52253         }
52254         var bt = this.getBodyTable(), lt = this.getLockedTable();
52255         if(bt.rows[index]){
52256             bt.firstChild.removeChild(bt.rows[index]);
52257         }
52258         if(lt.rows[index]){
52259             lt.firstChild.removeChild(lt.rows[index]);
52260         }
52261         if(isUpdate !== true){
52262             this.stripeRows(index);
52263             this.syncRowHeights(index, index);
52264             this.layout();
52265             this.fireEvent("rowremoved", this, index, record);
52266         }
52267     },
52268
52269     onLoad : function(){
52270         this.scrollToTop();
52271     },
52272
52273     /**
52274      * Scrolls the grid to the top
52275      */
52276     scrollToTop : function(){
52277         if(this.scroller){
52278             this.scroller.dom.scrollTop = 0;
52279             this.syncScroll();
52280         }
52281     },
52282
52283     /**
52284      * Gets a panel in the header of the grid that can be used for toolbars etc.
52285      * After modifying the contents of this panel a call to grid.autoSize() may be
52286      * required to register any changes in size.
52287      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52288      * @return Roo.Element
52289      */
52290     getHeaderPanel : function(doShow){
52291         if(doShow){
52292             this.headerPanel.show();
52293         }
52294         return this.headerPanel;
52295     },
52296
52297     /**
52298      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52299      * After modifying the contents of this panel a call to grid.autoSize() may be
52300      * required to register any changes in size.
52301      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52302      * @return Roo.Element
52303      */
52304     getFooterPanel : function(doShow){
52305         if(doShow){
52306             this.footerPanel.show();
52307         }
52308         return this.footerPanel;
52309     },
52310
52311     initElements : function(){
52312         var E = Roo.Element;
52313         var el = this.grid.getGridEl().dom.firstChild;
52314         var cs = el.childNodes;
52315
52316         this.el = new E(el);
52317         
52318          this.focusEl = new E(el.firstChild);
52319         this.focusEl.swallowEvent("click", true);
52320         
52321         this.headerPanel = new E(cs[1]);
52322         this.headerPanel.enableDisplayMode("block");
52323
52324         this.scroller = new E(cs[2]);
52325         this.scrollSizer = new E(this.scroller.dom.firstChild);
52326
52327         this.lockedWrap = new E(cs[3]);
52328         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52329         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52330
52331         this.mainWrap = new E(cs[4]);
52332         this.mainHd = new E(this.mainWrap.dom.firstChild);
52333         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52334
52335         this.footerPanel = new E(cs[5]);
52336         this.footerPanel.enableDisplayMode("block");
52337
52338         this.resizeProxy = new E(cs[6]);
52339
52340         this.headerSelector = String.format(
52341            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52342            this.lockedHd.id, this.mainHd.id
52343         );
52344
52345         this.splitterSelector = String.format(
52346            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52347            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52348         );
52349     },
52350     idToCssName : function(s)
52351     {
52352         return s.replace(/[^a-z0-9]+/ig, '-');
52353     },
52354
52355     getHeaderCell : function(index){
52356         return Roo.DomQuery.select(this.headerSelector)[index];
52357     },
52358
52359     getHeaderCellMeasure : function(index){
52360         return this.getHeaderCell(index).firstChild;
52361     },
52362
52363     getHeaderCellText : function(index){
52364         return this.getHeaderCell(index).firstChild.firstChild;
52365     },
52366
52367     getLockedTable : function(){
52368         return this.lockedBody.dom.firstChild;
52369     },
52370
52371     getBodyTable : function(){
52372         return this.mainBody.dom.firstChild;
52373     },
52374
52375     getLockedRow : function(index){
52376         return this.getLockedTable().rows[index];
52377     },
52378
52379     getRow : function(index){
52380         return this.getBodyTable().rows[index];
52381     },
52382
52383     getRowComposite : function(index){
52384         if(!this.rowEl){
52385             this.rowEl = new Roo.CompositeElementLite();
52386         }
52387         var els = [], lrow, mrow;
52388         if(lrow = this.getLockedRow(index)){
52389             els.push(lrow);
52390         }
52391         if(mrow = this.getRow(index)){
52392             els.push(mrow);
52393         }
52394         this.rowEl.elements = els;
52395         return this.rowEl;
52396     },
52397     /**
52398      * Gets the 'td' of the cell
52399      * 
52400      * @param {Integer} rowIndex row to select
52401      * @param {Integer} colIndex column to select
52402      * 
52403      * @return {Object} 
52404      */
52405     getCell : function(rowIndex, colIndex){
52406         var locked = this.cm.getLockedCount();
52407         var source;
52408         if(colIndex < locked){
52409             source = this.lockedBody.dom.firstChild;
52410         }else{
52411             source = this.mainBody.dom.firstChild;
52412             colIndex -= locked;
52413         }
52414         return source.rows[rowIndex].childNodes[colIndex];
52415     },
52416
52417     getCellText : function(rowIndex, colIndex){
52418         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52419     },
52420
52421     getCellBox : function(cell){
52422         var b = this.fly(cell).getBox();
52423         if(Roo.isOpera){ // opera fails to report the Y
52424             b.y = cell.offsetTop + this.mainBody.getY();
52425         }
52426         return b;
52427     },
52428
52429     getCellIndex : function(cell){
52430         var id = String(cell.className).match(this.cellRE);
52431         if(id){
52432             return parseInt(id[1], 10);
52433         }
52434         return 0;
52435     },
52436
52437     findHeaderIndex : function(n){
52438         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52439         return r ? this.getCellIndex(r) : false;
52440     },
52441
52442     findHeaderCell : function(n){
52443         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52444         return r ? r : false;
52445     },
52446
52447     findRowIndex : function(n){
52448         if(!n){
52449             return false;
52450         }
52451         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52452         return r ? r.rowIndex : false;
52453     },
52454
52455     findCellIndex : function(node){
52456         var stop = this.el.dom;
52457         while(node && node != stop){
52458             if(this.findRE.test(node.className)){
52459                 return this.getCellIndex(node);
52460             }
52461             node = node.parentNode;
52462         }
52463         return false;
52464     },
52465
52466     getColumnId : function(index){
52467         return this.cm.getColumnId(index);
52468     },
52469
52470     getSplitters : function()
52471     {
52472         if(this.splitterSelector){
52473            return Roo.DomQuery.select(this.splitterSelector);
52474         }else{
52475             return null;
52476       }
52477     },
52478
52479     getSplitter : function(index){
52480         return this.getSplitters()[index];
52481     },
52482
52483     onRowOver : function(e, t){
52484         var row;
52485         if((row = this.findRowIndex(t)) !== false){
52486             this.getRowComposite(row).addClass("x-grid-row-over");
52487         }
52488     },
52489
52490     onRowOut : function(e, t){
52491         var row;
52492         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52493             this.getRowComposite(row).removeClass("x-grid-row-over");
52494         }
52495     },
52496
52497     renderHeaders : function(){
52498         var cm = this.cm;
52499         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52500         var cb = [], lb = [], sb = [], lsb = [], p = {};
52501         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52502             p.cellId = "x-grid-hd-0-" + i;
52503             p.splitId = "x-grid-csplit-0-" + i;
52504             p.id = cm.getColumnId(i);
52505             p.title = cm.getColumnTooltip(i) || "";
52506             p.value = cm.getColumnHeader(i) || "";
52507             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52508             if(!cm.isLocked(i)){
52509                 cb[cb.length] = ct.apply(p);
52510                 sb[sb.length] = st.apply(p);
52511             }else{
52512                 lb[lb.length] = ct.apply(p);
52513                 lsb[lsb.length] = st.apply(p);
52514             }
52515         }
52516         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52517                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52518     },
52519
52520     updateHeaders : function(){
52521         var html = this.renderHeaders();
52522         this.lockedHd.update(html[0]);
52523         this.mainHd.update(html[1]);
52524     },
52525
52526     /**
52527      * Focuses the specified row.
52528      * @param {Number} row The row index
52529      */
52530     focusRow : function(row)
52531     {
52532         //Roo.log('GridView.focusRow');
52533         var x = this.scroller.dom.scrollLeft;
52534         this.focusCell(row, 0, false);
52535         this.scroller.dom.scrollLeft = x;
52536     },
52537
52538     /**
52539      * Focuses the specified cell.
52540      * @param {Number} row The row index
52541      * @param {Number} col The column index
52542      * @param {Boolean} hscroll false to disable horizontal scrolling
52543      */
52544     focusCell : function(row, col, hscroll)
52545     {
52546         //Roo.log('GridView.focusCell');
52547         var el = this.ensureVisible(row, col, hscroll);
52548         this.focusEl.alignTo(el, "tl-tl");
52549         if(Roo.isGecko){
52550             this.focusEl.focus();
52551         }else{
52552             this.focusEl.focus.defer(1, this.focusEl);
52553         }
52554     },
52555
52556     /**
52557      * Scrolls the specified cell into view
52558      * @param {Number} row The row index
52559      * @param {Number} col The column index
52560      * @param {Boolean} hscroll false to disable horizontal scrolling
52561      */
52562     ensureVisible : function(row, col, hscroll)
52563     {
52564         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52565         //return null; //disable for testing.
52566         if(typeof row != "number"){
52567             row = row.rowIndex;
52568         }
52569         if(row < 0 && row >= this.ds.getCount()){
52570             return  null;
52571         }
52572         col = (col !== undefined ? col : 0);
52573         var cm = this.grid.colModel;
52574         while(cm.isHidden(col)){
52575             col++;
52576         }
52577
52578         var el = this.getCell(row, col);
52579         if(!el){
52580             return null;
52581         }
52582         var c = this.scroller.dom;
52583
52584         var ctop = parseInt(el.offsetTop, 10);
52585         var cleft = parseInt(el.offsetLeft, 10);
52586         var cbot = ctop + el.offsetHeight;
52587         var cright = cleft + el.offsetWidth;
52588         
52589         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52590         var stop = parseInt(c.scrollTop, 10);
52591         var sleft = parseInt(c.scrollLeft, 10);
52592         var sbot = stop + ch;
52593         var sright = sleft + c.clientWidth;
52594         /*
52595         Roo.log('GridView.ensureVisible:' +
52596                 ' ctop:' + ctop +
52597                 ' c.clientHeight:' + c.clientHeight +
52598                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52599                 ' stop:' + stop +
52600                 ' cbot:' + cbot +
52601                 ' sbot:' + sbot +
52602                 ' ch:' + ch  
52603                 );
52604         */
52605         if(ctop < stop){
52606              c.scrollTop = ctop;
52607             //Roo.log("set scrolltop to ctop DISABLE?");
52608         }else if(cbot > sbot){
52609             //Roo.log("set scrolltop to cbot-ch");
52610             c.scrollTop = cbot-ch;
52611         }
52612         
52613         if(hscroll !== false){
52614             if(cleft < sleft){
52615                 c.scrollLeft = cleft;
52616             }else if(cright > sright){
52617                 c.scrollLeft = cright-c.clientWidth;
52618             }
52619         }
52620          
52621         return el;
52622     },
52623
52624     updateColumns : function(){
52625         this.grid.stopEditing();
52626         var cm = this.grid.colModel, colIds = this.getColumnIds();
52627         //var totalWidth = cm.getTotalWidth();
52628         var pos = 0;
52629         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52630             //if(cm.isHidden(i)) continue;
52631             var w = cm.getColumnWidth(i);
52632             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52633             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52634         }
52635         this.updateSplitters();
52636     },
52637
52638     generateRules : function(cm){
52639         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52640         Roo.util.CSS.removeStyleSheet(rulesId);
52641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52642             var cid = cm.getColumnId(i);
52643             var align = '';
52644             if(cm.config[i].align){
52645                 align = 'text-align:'+cm.config[i].align+';';
52646             }
52647             var hidden = '';
52648             if(cm.isHidden(i)){
52649                 hidden = 'display:none;';
52650             }
52651             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52652             ruleBuf.push(
52653                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52654                     this.hdSelector, cid, " {\n", align, width, "}\n",
52655                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52656                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52657         }
52658         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52659     },
52660
52661     updateSplitters : function(){
52662         var cm = this.cm, s = this.getSplitters();
52663         if(s){ // splitters not created yet
52664             var pos = 0, locked = true;
52665             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52666                 if(cm.isHidden(i)) continue;
52667                 var w = cm.getColumnWidth(i); // make sure it's a number
52668                 if(!cm.isLocked(i) && locked){
52669                     pos = 0;
52670                     locked = false;
52671                 }
52672                 pos += w;
52673                 s[i].style.left = (pos-this.splitOffset) + "px";
52674             }
52675         }
52676     },
52677
52678     handleHiddenChange : function(colModel, colIndex, hidden){
52679         if(hidden){
52680             this.hideColumn(colIndex);
52681         }else{
52682             this.unhideColumn(colIndex);
52683         }
52684     },
52685
52686     hideColumn : function(colIndex){
52687         var cid = this.getColumnId(colIndex);
52688         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52689         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52690         if(Roo.isSafari){
52691             this.updateHeaders();
52692         }
52693         this.updateSplitters();
52694         this.layout();
52695     },
52696
52697     unhideColumn : function(colIndex){
52698         var cid = this.getColumnId(colIndex);
52699         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52700         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52701
52702         if(Roo.isSafari){
52703             this.updateHeaders();
52704         }
52705         this.updateSplitters();
52706         this.layout();
52707     },
52708
52709     insertRows : function(dm, firstRow, lastRow, isUpdate){
52710         if(firstRow == 0 && lastRow == dm.getCount()-1){
52711             this.refresh();
52712         }else{
52713             if(!isUpdate){
52714                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52715             }
52716             var s = this.getScrollState();
52717             var markup = this.renderRows(firstRow, lastRow);
52718             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52719             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52720             this.restoreScroll(s);
52721             if(!isUpdate){
52722                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52723                 this.syncRowHeights(firstRow, lastRow);
52724                 this.stripeRows(firstRow);
52725                 this.layout();
52726             }
52727         }
52728     },
52729
52730     bufferRows : function(markup, target, index){
52731         var before = null, trows = target.rows, tbody = target.tBodies[0];
52732         if(index < trows.length){
52733             before = trows[index];
52734         }
52735         var b = document.createElement("div");
52736         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52737         var rows = b.firstChild.rows;
52738         for(var i = 0, len = rows.length; i < len; i++){
52739             if(before){
52740                 tbody.insertBefore(rows[0], before);
52741             }else{
52742                 tbody.appendChild(rows[0]);
52743             }
52744         }
52745         b.innerHTML = "";
52746         b = null;
52747     },
52748
52749     deleteRows : function(dm, firstRow, lastRow){
52750         if(dm.getRowCount()<1){
52751             this.fireEvent("beforerefresh", this);
52752             this.mainBody.update("");
52753             this.lockedBody.update("");
52754             this.fireEvent("refresh", this);
52755         }else{
52756             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52757             var bt = this.getBodyTable();
52758             var tbody = bt.firstChild;
52759             var rows = bt.rows;
52760             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52761                 tbody.removeChild(rows[firstRow]);
52762             }
52763             this.stripeRows(firstRow);
52764             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52765         }
52766     },
52767
52768     updateRows : function(dataSource, firstRow, lastRow){
52769         var s = this.getScrollState();
52770         this.refresh();
52771         this.restoreScroll(s);
52772     },
52773
52774     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52775         if(!noRefresh){
52776            this.refresh();
52777         }
52778         this.updateHeaderSortState();
52779     },
52780
52781     getScrollState : function(){
52782         
52783         var sb = this.scroller.dom;
52784         return {left: sb.scrollLeft, top: sb.scrollTop};
52785     },
52786
52787     stripeRows : function(startRow){
52788         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52789             return;
52790         }
52791         startRow = startRow || 0;
52792         var rows = this.getBodyTable().rows;
52793         var lrows = this.getLockedTable().rows;
52794         var cls = ' x-grid-row-alt ';
52795         for(var i = startRow, len = rows.length; i < len; i++){
52796             var row = rows[i], lrow = lrows[i];
52797             var isAlt = ((i+1) % 2 == 0);
52798             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52799             if(isAlt == hasAlt){
52800                 continue;
52801             }
52802             if(isAlt){
52803                 row.className += " x-grid-row-alt";
52804             }else{
52805                 row.className = row.className.replace("x-grid-row-alt", "");
52806             }
52807             if(lrow){
52808                 lrow.className = row.className;
52809             }
52810         }
52811     },
52812
52813     restoreScroll : function(state){
52814         //Roo.log('GridView.restoreScroll');
52815         var sb = this.scroller.dom;
52816         sb.scrollLeft = state.left;
52817         sb.scrollTop = state.top;
52818         this.syncScroll();
52819     },
52820
52821     syncScroll : function(){
52822         //Roo.log('GridView.syncScroll');
52823         var sb = this.scroller.dom;
52824         var sh = this.mainHd.dom;
52825         var bs = this.mainBody.dom;
52826         var lv = this.lockedBody.dom;
52827         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52828         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52829     },
52830
52831     handleScroll : function(e){
52832         this.syncScroll();
52833         var sb = this.scroller.dom;
52834         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52835         e.stopEvent();
52836     },
52837
52838     handleWheel : function(e){
52839         var d = e.getWheelDelta();
52840         this.scroller.dom.scrollTop -= d*22;
52841         // set this here to prevent jumpy scrolling on large tables
52842         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52843         e.stopEvent();
52844     },
52845
52846     renderRows : function(startRow, endRow){
52847         // pull in all the crap needed to render rows
52848         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52849         var colCount = cm.getColumnCount();
52850
52851         if(ds.getCount() < 1){
52852             return ["", ""];
52853         }
52854
52855         // build a map for all the columns
52856         var cs = [];
52857         for(var i = 0; i < colCount; i++){
52858             var name = cm.getDataIndex(i);
52859             cs[i] = {
52860                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52861                 renderer : cm.getRenderer(i),
52862                 id : cm.getColumnId(i),
52863                 locked : cm.isLocked(i)
52864             };
52865         }
52866
52867         startRow = startRow || 0;
52868         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52869
52870         // records to render
52871         var rs = ds.getRange(startRow, endRow);
52872
52873         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52874     },
52875
52876     // As much as I hate to duplicate code, this was branched because FireFox really hates
52877     // [].join("") on strings. The performance difference was substantial enough to
52878     // branch this function
52879     doRender : Roo.isGecko ?
52880             function(cs, rs, ds, startRow, colCount, stripe){
52881                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52882                 // buffers
52883                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52884                 
52885                 var hasListener = this.grid.hasListener('rowclass');
52886                 var rowcfg = {};
52887                 for(var j = 0, len = rs.length; j < len; j++){
52888                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52889                     for(var i = 0; i < colCount; i++){
52890                         c = cs[i];
52891                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52892                         p.id = c.id;
52893                         p.css = p.attr = "";
52894                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52895                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52896                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52897                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52898                         }
52899                         var markup = ct.apply(p);
52900                         if(!c.locked){
52901                             cb+= markup;
52902                         }else{
52903                             lcb+= markup;
52904                         }
52905                     }
52906                     var alt = [];
52907                     if(stripe && ((rowIndex+1) % 2 == 0)){
52908                         alt.push("x-grid-row-alt")
52909                     }
52910                     if(r.dirty){
52911                         alt.push(  " x-grid-dirty-row");
52912                     }
52913                     rp.cells = lcb;
52914                     if(this.getRowClass){
52915                         alt.push(this.getRowClass(r, rowIndex));
52916                     }
52917                     if (hasListener) {
52918                         rowcfg = {
52919                              
52920                             record: r,
52921                             rowIndex : rowIndex,
52922                             rowClass : ''
52923                         }
52924                         this.grid.fireEvent('rowclass', this, rowcfg);
52925                         alt.push(rowcfg.rowClass);
52926                     }
52927                     rp.alt = alt.join(" ");
52928                     lbuf+= rt.apply(rp);
52929                     rp.cells = cb;
52930                     buf+=  rt.apply(rp);
52931                 }
52932                 return [lbuf, buf];
52933             } :
52934             function(cs, rs, ds, startRow, colCount, stripe){
52935                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52936                 // buffers
52937                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52938                 var hasListener = this.grid.hasListener('rowclass');
52939  
52940                 var rowcfg = {};
52941                 for(var j = 0, len = rs.length; j < len; j++){
52942                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52943                     for(var i = 0; i < colCount; i++){
52944                         c = cs[i];
52945                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52946                         p.id = c.id;
52947                         p.css = p.attr = "";
52948                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52949                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52950                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52951                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52952                         }
52953                         
52954                         var markup = ct.apply(p);
52955                         if(!c.locked){
52956                             cb[cb.length] = markup;
52957                         }else{
52958                             lcb[lcb.length] = markup;
52959                         }
52960                     }
52961                     var alt = [];
52962                     if(stripe && ((rowIndex+1) % 2 == 0)){
52963                         alt.push( "x-grid-row-alt");
52964                     }
52965                     if(r.dirty){
52966                         alt.push(" x-grid-dirty-row");
52967                     }
52968                     rp.cells = lcb;
52969                     if(this.getRowClass){
52970                         alt.push( this.getRowClass(r, rowIndex));
52971                     }
52972                     if (hasListener) {
52973                         rowcfg = {
52974                              
52975                             record: r,
52976                             rowIndex : rowIndex,
52977                             rowClass : ''
52978                         }
52979                         this.grid.fireEvent('rowclass', this, rowcfg);
52980                         alt.push(rowcfg.rowClass);
52981                     }
52982                     rp.alt = alt.join(" ");
52983                     rp.cells = lcb.join("");
52984                     lbuf[lbuf.length] = rt.apply(rp);
52985                     rp.cells = cb.join("");
52986                     buf[buf.length] =  rt.apply(rp);
52987                 }
52988                 return [lbuf.join(""), buf.join("")];
52989             },
52990
52991     renderBody : function(){
52992         var markup = this.renderRows();
52993         var bt = this.templates.body;
52994         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
52995     },
52996
52997     /**
52998      * Refreshes the grid
52999      * @param {Boolean} headersToo
53000      */
53001     refresh : function(headersToo){
53002         this.fireEvent("beforerefresh", this);
53003         this.grid.stopEditing();
53004         var result = this.renderBody();
53005         this.lockedBody.update(result[0]);
53006         this.mainBody.update(result[1]);
53007         if(headersToo === true){
53008             this.updateHeaders();
53009             this.updateColumns();
53010             this.updateSplitters();
53011             this.updateHeaderSortState();
53012         }
53013         this.syncRowHeights();
53014         this.layout();
53015         this.fireEvent("refresh", this);
53016     },
53017
53018     handleColumnMove : function(cm, oldIndex, newIndex){
53019         this.indexMap = null;
53020         var s = this.getScrollState();
53021         this.refresh(true);
53022         this.restoreScroll(s);
53023         this.afterMove(newIndex);
53024     },
53025
53026     afterMove : function(colIndex){
53027         if(this.enableMoveAnim && Roo.enableFx){
53028             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53029         }
53030         // if multisort - fix sortOrder, and reload..
53031         if (this.grid.dataSource.multiSort) {
53032             // the we can call sort again..
53033             var dm = this.grid.dataSource;
53034             var cm = this.grid.colModel;
53035             var so = [];
53036             for(var i = 0; i < cm.config.length; i++ ) {
53037                 
53038                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53039                     continue; // dont' bother, it's not in sort list or being set.
53040                 }
53041                 
53042                 so.push(cm.config[i].dataIndex);
53043             };
53044             dm.sortOrder = so;
53045             dm.load(dm.lastOptions);
53046             
53047             
53048         }
53049         
53050     },
53051
53052     updateCell : function(dm, rowIndex, dataIndex){
53053         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53054         if(typeof colIndex == "undefined"){ // not present in grid
53055             return;
53056         }
53057         var cm = this.grid.colModel;
53058         var cell = this.getCell(rowIndex, colIndex);
53059         var cellText = this.getCellText(rowIndex, colIndex);
53060
53061         var p = {
53062             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53063             id : cm.getColumnId(colIndex),
53064             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53065         };
53066         var renderer = cm.getRenderer(colIndex);
53067         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53068         if(typeof val == "undefined" || val === "") val = "&#160;";
53069         cellText.innerHTML = val;
53070         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53071         this.syncRowHeights(rowIndex, rowIndex);
53072     },
53073
53074     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53075         var maxWidth = 0;
53076         if(this.grid.autoSizeHeaders){
53077             var h = this.getHeaderCellMeasure(colIndex);
53078             maxWidth = Math.max(maxWidth, h.scrollWidth);
53079         }
53080         var tb, index;
53081         if(this.cm.isLocked(colIndex)){
53082             tb = this.getLockedTable();
53083             index = colIndex;
53084         }else{
53085             tb = this.getBodyTable();
53086             index = colIndex - this.cm.getLockedCount();
53087         }
53088         if(tb && tb.rows){
53089             var rows = tb.rows;
53090             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53091             for(var i = 0; i < stopIndex; i++){
53092                 var cell = rows[i].childNodes[index].firstChild;
53093                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53094             }
53095         }
53096         return maxWidth + /*margin for error in IE*/ 5;
53097     },
53098     /**
53099      * Autofit a column to its content.
53100      * @param {Number} colIndex
53101      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53102      */
53103      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53104          if(this.cm.isHidden(colIndex)){
53105              return; // can't calc a hidden column
53106          }
53107         if(forceMinSize){
53108             var cid = this.cm.getColumnId(colIndex);
53109             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53110            if(this.grid.autoSizeHeaders){
53111                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53112            }
53113         }
53114         var newWidth = this.calcColumnWidth(colIndex);
53115         this.cm.setColumnWidth(colIndex,
53116             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53117         if(!suppressEvent){
53118             this.grid.fireEvent("columnresize", colIndex, newWidth);
53119         }
53120     },
53121
53122     /**
53123      * Autofits all columns to their content and then expands to fit any extra space in the grid
53124      */
53125      autoSizeColumns : function(){
53126         var cm = this.grid.colModel;
53127         var colCount = cm.getColumnCount();
53128         for(var i = 0; i < colCount; i++){
53129             this.autoSizeColumn(i, true, true);
53130         }
53131         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53132             this.fitColumns();
53133         }else{
53134             this.updateColumns();
53135             this.layout();
53136         }
53137     },
53138
53139     /**
53140      * Autofits all columns to the grid's width proportionate with their current size
53141      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53142      */
53143     fitColumns : function(reserveScrollSpace){
53144         var cm = this.grid.colModel;
53145         var colCount = cm.getColumnCount();
53146         var cols = [];
53147         var width = 0;
53148         var i, w;
53149         for (i = 0; i < colCount; i++){
53150             if(!cm.isHidden(i) && !cm.isFixed(i)){
53151                 w = cm.getColumnWidth(i);
53152                 cols.push(i);
53153                 cols.push(w);
53154                 width += w;
53155             }
53156         }
53157         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53158         if(reserveScrollSpace){
53159             avail -= 17;
53160         }
53161         var frac = (avail - cm.getTotalWidth())/width;
53162         while (cols.length){
53163             w = cols.pop();
53164             i = cols.pop();
53165             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53166         }
53167         this.updateColumns();
53168         this.layout();
53169     },
53170
53171     onRowSelect : function(rowIndex){
53172         var row = this.getRowComposite(rowIndex);
53173         row.addClass("x-grid-row-selected");
53174     },
53175
53176     onRowDeselect : function(rowIndex){
53177         var row = this.getRowComposite(rowIndex);
53178         row.removeClass("x-grid-row-selected");
53179     },
53180
53181     onCellSelect : function(row, col){
53182         var cell = this.getCell(row, col);
53183         if(cell){
53184             Roo.fly(cell).addClass("x-grid-cell-selected");
53185         }
53186     },
53187
53188     onCellDeselect : function(row, col){
53189         var cell = this.getCell(row, col);
53190         if(cell){
53191             Roo.fly(cell).removeClass("x-grid-cell-selected");
53192         }
53193     },
53194
53195     updateHeaderSortState : function(){
53196         
53197         // sort state can be single { field: xxx, direction : yyy}
53198         // or   { xxx=>ASC , yyy : DESC ..... }
53199         
53200         var mstate = {};
53201         if (!this.ds.multiSort) { 
53202             var state = this.ds.getSortState();
53203             if(!state){
53204                 return;
53205             }
53206             mstate[state.field] = state.direction;
53207             // FIXME... - this is not used here.. but might be elsewhere..
53208             this.sortState = state;
53209             
53210         } else {
53211             mstate = this.ds.sortToggle;
53212         }
53213         //remove existing sort classes..
53214         
53215         var sc = this.sortClasses;
53216         var hds = this.el.select(this.headerSelector).removeClass(sc);
53217         
53218         for(var f in mstate) {
53219         
53220             var sortColumn = this.cm.findColumnIndex(f);
53221             
53222             if(sortColumn != -1){
53223                 var sortDir = mstate[f];        
53224                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53225             }
53226         }
53227         
53228          
53229         
53230     },
53231
53232
53233     handleHeaderClick : function(g, index){
53234         if(this.headersDisabled){
53235             return;
53236         }
53237         var dm = g.dataSource, cm = g.colModel;
53238         if(!cm.isSortable(index)){
53239             return;
53240         }
53241         g.stopEditing();
53242         
53243         if (dm.multiSort) {
53244             // update the sortOrder
53245             var so = [];
53246             for(var i = 0; i < cm.config.length; i++ ) {
53247                 
53248                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53249                     continue; // dont' bother, it's not in sort list or being set.
53250                 }
53251                 
53252                 so.push(cm.config[i].dataIndex);
53253             };
53254             dm.sortOrder = so;
53255         }
53256         
53257         
53258         dm.sort(cm.getDataIndex(index));
53259     },
53260
53261
53262     destroy : function(){
53263         if(this.colMenu){
53264             this.colMenu.removeAll();
53265             Roo.menu.MenuMgr.unregister(this.colMenu);
53266             this.colMenu.getEl().remove();
53267             delete this.colMenu;
53268         }
53269         if(this.hmenu){
53270             this.hmenu.removeAll();
53271             Roo.menu.MenuMgr.unregister(this.hmenu);
53272             this.hmenu.getEl().remove();
53273             delete this.hmenu;
53274         }
53275         if(this.grid.enableColumnMove){
53276             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53277             if(dds){
53278                 for(var dd in dds){
53279                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53280                         var elid = dds[dd].dragElId;
53281                         dds[dd].unreg();
53282                         Roo.get(elid).remove();
53283                     } else if(dds[dd].config.isTarget){
53284                         dds[dd].proxyTop.remove();
53285                         dds[dd].proxyBottom.remove();
53286                         dds[dd].unreg();
53287                     }
53288                     if(Roo.dd.DDM.locationCache[dd]){
53289                         delete Roo.dd.DDM.locationCache[dd];
53290                     }
53291                 }
53292                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53293             }
53294         }
53295         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53296         this.bind(null, null);
53297         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53298     },
53299
53300     handleLockChange : function(){
53301         this.refresh(true);
53302     },
53303
53304     onDenyColumnLock : function(){
53305
53306     },
53307
53308     onDenyColumnHide : function(){
53309
53310     },
53311
53312     handleHdMenuClick : function(item){
53313         var index = this.hdCtxIndex;
53314         var cm = this.cm, ds = this.ds;
53315         switch(item.id){
53316             case "asc":
53317                 ds.sort(cm.getDataIndex(index), "ASC");
53318                 break;
53319             case "desc":
53320                 ds.sort(cm.getDataIndex(index), "DESC");
53321                 break;
53322             case "lock":
53323                 var lc = cm.getLockedCount();
53324                 if(cm.getColumnCount(true) <= lc+1){
53325                     this.onDenyColumnLock();
53326                     return;
53327                 }
53328                 if(lc != index){
53329                     cm.setLocked(index, true, true);
53330                     cm.moveColumn(index, lc);
53331                     this.grid.fireEvent("columnmove", index, lc);
53332                 }else{
53333                     cm.setLocked(index, true);
53334                 }
53335             break;
53336             case "unlock":
53337                 var lc = cm.getLockedCount();
53338                 if((lc-1) != index){
53339                     cm.setLocked(index, false, true);
53340                     cm.moveColumn(index, lc-1);
53341                     this.grid.fireEvent("columnmove", index, lc-1);
53342                 }else{
53343                     cm.setLocked(index, false);
53344                 }
53345             break;
53346             default:
53347                 index = cm.getIndexById(item.id.substr(4));
53348                 if(index != -1){
53349                     if(item.checked && cm.getColumnCount(true) <= 1){
53350                         this.onDenyColumnHide();
53351                         return false;
53352                     }
53353                     cm.setHidden(index, item.checked);
53354                 }
53355         }
53356         return true;
53357     },
53358
53359     beforeColMenuShow : function(){
53360         var cm = this.cm,  colCount = cm.getColumnCount();
53361         this.colMenu.removeAll();
53362         for(var i = 0; i < colCount; i++){
53363             this.colMenu.add(new Roo.menu.CheckItem({
53364                 id: "col-"+cm.getColumnId(i),
53365                 text: cm.getColumnHeader(i),
53366                 checked: !cm.isHidden(i),
53367                 hideOnClick:false
53368             }));
53369         }
53370     },
53371
53372     handleHdCtx : function(g, index, e){
53373         e.stopEvent();
53374         var hd = this.getHeaderCell(index);
53375         this.hdCtxIndex = index;
53376         var ms = this.hmenu.items, cm = this.cm;
53377         ms.get("asc").setDisabled(!cm.isSortable(index));
53378         ms.get("desc").setDisabled(!cm.isSortable(index));
53379         if(this.grid.enableColLock !== false){
53380             ms.get("lock").setDisabled(cm.isLocked(index));
53381             ms.get("unlock").setDisabled(!cm.isLocked(index));
53382         }
53383         this.hmenu.show(hd, "tl-bl");
53384     },
53385
53386     handleHdOver : function(e){
53387         var hd = this.findHeaderCell(e.getTarget());
53388         if(hd && !this.headersDisabled){
53389             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53390                this.fly(hd).addClass("x-grid-hd-over");
53391             }
53392         }
53393     },
53394
53395     handleHdOut : function(e){
53396         var hd = this.findHeaderCell(e.getTarget());
53397         if(hd){
53398             this.fly(hd).removeClass("x-grid-hd-over");
53399         }
53400     },
53401
53402     handleSplitDblClick : function(e, t){
53403         var i = this.getCellIndex(t);
53404         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53405             this.autoSizeColumn(i, true);
53406             this.layout();
53407         }
53408     },
53409
53410     render : function(){
53411
53412         var cm = this.cm;
53413         var colCount = cm.getColumnCount();
53414
53415         if(this.grid.monitorWindowResize === true){
53416             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53417         }
53418         var header = this.renderHeaders();
53419         var body = this.templates.body.apply({rows:""});
53420         var html = this.templates.master.apply({
53421             lockedBody: body,
53422             body: body,
53423             lockedHeader: header[0],
53424             header: header[1]
53425         });
53426
53427         //this.updateColumns();
53428
53429         this.grid.getGridEl().dom.innerHTML = html;
53430
53431         this.initElements();
53432         
53433         // a kludge to fix the random scolling effect in webkit
53434         this.el.on("scroll", function() {
53435             this.el.dom.scrollTop=0; // hopefully not recursive..
53436         },this);
53437
53438         this.scroller.on("scroll", this.handleScroll, this);
53439         this.lockedBody.on("mousewheel", this.handleWheel, this);
53440         this.mainBody.on("mousewheel", this.handleWheel, this);
53441
53442         this.mainHd.on("mouseover", this.handleHdOver, this);
53443         this.mainHd.on("mouseout", this.handleHdOut, this);
53444         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53445                 {delegate: "."+this.splitClass});
53446
53447         this.lockedHd.on("mouseover", this.handleHdOver, this);
53448         this.lockedHd.on("mouseout", this.handleHdOut, this);
53449         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53450                 {delegate: "."+this.splitClass});
53451
53452         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53453             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53454         }
53455
53456         this.updateSplitters();
53457
53458         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53459             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53460             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53461         }
53462
53463         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53464             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53465             this.hmenu.add(
53466                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53467                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53468             );
53469             if(this.grid.enableColLock !== false){
53470                 this.hmenu.add('-',
53471                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53472                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53473                 );
53474             }
53475             if(this.grid.enableColumnHide !== false){
53476
53477                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53478                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53479                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53480
53481                 this.hmenu.add('-',
53482                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53483                 );
53484             }
53485             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53486
53487             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53488         }
53489
53490         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53491             this.dd = new Roo.grid.GridDragZone(this.grid, {
53492                 ddGroup : this.grid.ddGroup || 'GridDD'
53493             });
53494             
53495         }
53496
53497         /*
53498         for(var i = 0; i < colCount; i++){
53499             if(cm.isHidden(i)){
53500                 this.hideColumn(i);
53501             }
53502             if(cm.config[i].align){
53503                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53504                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53505             }
53506         }*/
53507         
53508         this.updateHeaderSortState();
53509
53510         this.beforeInitialResize();
53511         this.layout(true);
53512
53513         // two part rendering gives faster view to the user
53514         this.renderPhase2.defer(1, this);
53515     },
53516
53517     renderPhase2 : function(){
53518         // render the rows now
53519         this.refresh();
53520         if(this.grid.autoSizeColumns){
53521             this.autoSizeColumns();
53522         }
53523     },
53524
53525     beforeInitialResize : function(){
53526
53527     },
53528
53529     onColumnSplitterMoved : function(i, w){
53530         this.userResized = true;
53531         var cm = this.grid.colModel;
53532         cm.setColumnWidth(i, w, true);
53533         var cid = cm.getColumnId(i);
53534         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53535         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53536         this.updateSplitters();
53537         this.layout();
53538         this.grid.fireEvent("columnresize", i, w);
53539     },
53540
53541     syncRowHeights : function(startIndex, endIndex){
53542         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53543             startIndex = startIndex || 0;
53544             var mrows = this.getBodyTable().rows;
53545             var lrows = this.getLockedTable().rows;
53546             var len = mrows.length-1;
53547             endIndex = Math.min(endIndex || len, len);
53548             for(var i = startIndex; i <= endIndex; i++){
53549                 var m = mrows[i], l = lrows[i];
53550                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53551                 m.style.height = l.style.height = h + "px";
53552             }
53553         }
53554     },
53555
53556     layout : function(initialRender, is2ndPass){
53557         var g = this.grid;
53558         var auto = g.autoHeight;
53559         var scrollOffset = 16;
53560         var c = g.getGridEl(), cm = this.cm,
53561                 expandCol = g.autoExpandColumn,
53562                 gv = this;
53563         //c.beginMeasure();
53564
53565         if(!c.dom.offsetWidth){ // display:none?
53566             if(initialRender){
53567                 this.lockedWrap.show();
53568                 this.mainWrap.show();
53569             }
53570             return;
53571         }
53572
53573         var hasLock = this.cm.isLocked(0);
53574
53575         var tbh = this.headerPanel.getHeight();
53576         var bbh = this.footerPanel.getHeight();
53577
53578         if(auto){
53579             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53580             var newHeight = ch + c.getBorderWidth("tb");
53581             if(g.maxHeight){
53582                 newHeight = Math.min(g.maxHeight, newHeight);
53583             }
53584             c.setHeight(newHeight);
53585         }
53586
53587         if(g.autoWidth){
53588             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53589         }
53590
53591         var s = this.scroller;
53592
53593         var csize = c.getSize(true);
53594
53595         this.el.setSize(csize.width, csize.height);
53596
53597         this.headerPanel.setWidth(csize.width);
53598         this.footerPanel.setWidth(csize.width);
53599
53600         var hdHeight = this.mainHd.getHeight();
53601         var vw = csize.width;
53602         var vh = csize.height - (tbh + bbh);
53603
53604         s.setSize(vw, vh);
53605
53606         var bt = this.getBodyTable();
53607         var ltWidth = hasLock ?
53608                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53609
53610         var scrollHeight = bt.offsetHeight;
53611         var scrollWidth = ltWidth + bt.offsetWidth;
53612         var vscroll = false, hscroll = false;
53613
53614         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53615
53616         var lw = this.lockedWrap, mw = this.mainWrap;
53617         var lb = this.lockedBody, mb = this.mainBody;
53618
53619         setTimeout(function(){
53620             var t = s.dom.offsetTop;
53621             var w = s.dom.clientWidth,
53622                 h = s.dom.clientHeight;
53623
53624             lw.setTop(t);
53625             lw.setSize(ltWidth, h);
53626
53627             mw.setLeftTop(ltWidth, t);
53628             mw.setSize(w-ltWidth, h);
53629
53630             lb.setHeight(h-hdHeight);
53631             mb.setHeight(h-hdHeight);
53632
53633             if(is2ndPass !== true && !gv.userResized && expandCol){
53634                 // high speed resize without full column calculation
53635                 
53636                 var ci = cm.getIndexById(expandCol);
53637                 if (ci < 0) {
53638                     ci = cm.findColumnIndex(expandCol);
53639                 }
53640                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53641                 var expandId = cm.getColumnId(ci);
53642                 var  tw = cm.getTotalWidth(false);
53643                 var currentWidth = cm.getColumnWidth(ci);
53644                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53645                 if(currentWidth != cw){
53646                     cm.setColumnWidth(ci, cw, true);
53647                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53648                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53649                     gv.updateSplitters();
53650                     gv.layout(false, true);
53651                 }
53652             }
53653
53654             if(initialRender){
53655                 lw.show();
53656                 mw.show();
53657             }
53658             //c.endMeasure();
53659         }, 10);
53660     },
53661
53662     onWindowResize : function(){
53663         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53664             return;
53665         }
53666         this.layout();
53667     },
53668
53669     appendFooter : function(parentEl){
53670         return null;
53671     },
53672
53673     sortAscText : "Sort Ascending",
53674     sortDescText : "Sort Descending",
53675     lockText : "Lock Column",
53676     unlockText : "Unlock Column",
53677     columnsText : "Columns"
53678 });
53679
53680
53681 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53682     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53683     this.proxy.el.addClass('x-grid3-col-dd');
53684 };
53685
53686 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53687     handleMouseDown : function(e){
53688
53689     },
53690
53691     callHandleMouseDown : function(e){
53692         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53693     }
53694 });
53695 /*
53696  * Based on:
53697  * Ext JS Library 1.1.1
53698  * Copyright(c) 2006-2007, Ext JS, LLC.
53699  *
53700  * Originally Released Under LGPL - original licence link has changed is not relivant.
53701  *
53702  * Fork - LGPL
53703  * <script type="text/javascript">
53704  */
53705  
53706 // private
53707 // This is a support class used internally by the Grid components
53708 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53709     this.grid = grid;
53710     this.view = grid.getView();
53711     this.proxy = this.view.resizeProxy;
53712     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53713         "gridSplitters" + this.grid.getGridEl().id, {
53714         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53715     });
53716     this.setHandleElId(Roo.id(hd));
53717     this.setOuterHandleElId(Roo.id(hd2));
53718     this.scroll = false;
53719 };
53720 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53721     fly: Roo.Element.fly,
53722
53723     b4StartDrag : function(x, y){
53724         this.view.headersDisabled = true;
53725         this.proxy.setHeight(this.view.mainWrap.getHeight());
53726         var w = this.cm.getColumnWidth(this.cellIndex);
53727         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53728         this.resetConstraints();
53729         this.setXConstraint(minw, 1000);
53730         this.setYConstraint(0, 0);
53731         this.minX = x - minw;
53732         this.maxX = x + 1000;
53733         this.startPos = x;
53734         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53735     },
53736
53737
53738     handleMouseDown : function(e){
53739         ev = Roo.EventObject.setEvent(e);
53740         var t = this.fly(ev.getTarget());
53741         if(t.hasClass("x-grid-split")){
53742             this.cellIndex = this.view.getCellIndex(t.dom);
53743             this.split = t.dom;
53744             this.cm = this.grid.colModel;
53745             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53746                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53747             }
53748         }
53749     },
53750
53751     endDrag : function(e){
53752         this.view.headersDisabled = false;
53753         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53754         var diff = endX - this.startPos;
53755         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53756     },
53757
53758     autoOffset : function(){
53759         this.setDelta(0,0);
53760     }
53761 });/*
53762  * Based on:
53763  * Ext JS Library 1.1.1
53764  * Copyright(c) 2006-2007, Ext JS, LLC.
53765  *
53766  * Originally Released Under LGPL - original licence link has changed is not relivant.
53767  *
53768  * Fork - LGPL
53769  * <script type="text/javascript">
53770  */
53771  
53772 // private
53773 // This is a support class used internally by the Grid components
53774 Roo.grid.GridDragZone = function(grid, config){
53775     this.view = grid.getView();
53776     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53777     if(this.view.lockedBody){
53778         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53779         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53780     }
53781     this.scroll = false;
53782     this.grid = grid;
53783     this.ddel = document.createElement('div');
53784     this.ddel.className = 'x-grid-dd-wrap';
53785 };
53786
53787 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53788     ddGroup : "GridDD",
53789
53790     getDragData : function(e){
53791         var t = Roo.lib.Event.getTarget(e);
53792         var rowIndex = this.view.findRowIndex(t);
53793         var sm = this.grid.selModel;
53794             
53795         //Roo.log(rowIndex);
53796         
53797         if (sm.getSelectedCell) {
53798             // cell selection..
53799             if (!sm.getSelectedCell()) {
53800                 return false;
53801             }
53802             if (rowIndex != sm.getSelectedCell()[0]) {
53803                 return false;
53804             }
53805         
53806         }
53807         
53808         if(rowIndex !== false){
53809             
53810             // if editorgrid.. 
53811             
53812             
53813             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53814                
53815             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53816               //  
53817             //}
53818             if (e.hasModifier()){
53819                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53820             }
53821             
53822             Roo.log("getDragData");
53823             
53824             return {
53825                 grid: this.grid,
53826                 ddel: this.ddel,
53827                 rowIndex: rowIndex,
53828                 selections:sm.getSelections ? sm.getSelections() : (
53829                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53830                 )
53831             };
53832         }
53833         return false;
53834     },
53835
53836     onInitDrag : function(e){
53837         var data = this.dragData;
53838         this.ddel.innerHTML = this.grid.getDragDropText();
53839         this.proxy.update(this.ddel);
53840         // fire start drag?
53841     },
53842
53843     afterRepair : function(){
53844         this.dragging = false;
53845     },
53846
53847     getRepairXY : function(e, data){
53848         return false;
53849     },
53850
53851     onEndDrag : function(data, e){
53852         // fire end drag?
53853     },
53854
53855     onValidDrop : function(dd, e, id){
53856         // fire drag drop?
53857         this.hideProxy();
53858     },
53859
53860     beforeInvalidDrop : function(e, id){
53861
53862     }
53863 });/*
53864  * Based on:
53865  * Ext JS Library 1.1.1
53866  * Copyright(c) 2006-2007, Ext JS, LLC.
53867  *
53868  * Originally Released Under LGPL - original licence link has changed is not relivant.
53869  *
53870  * Fork - LGPL
53871  * <script type="text/javascript">
53872  */
53873  
53874
53875 /**
53876  * @class Roo.grid.ColumnModel
53877  * @extends Roo.util.Observable
53878  * This is the default implementation of a ColumnModel used by the Grid. It defines
53879  * the columns in the grid.
53880  * <br>Usage:<br>
53881  <pre><code>
53882  var colModel = new Roo.grid.ColumnModel([
53883         {header: "Ticker", width: 60, sortable: true, locked: true},
53884         {header: "Company Name", width: 150, sortable: true},
53885         {header: "Market Cap.", width: 100, sortable: true},
53886         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53887         {header: "Employees", width: 100, sortable: true, resizable: false}
53888  ]);
53889  </code></pre>
53890  * <p>
53891  
53892  * The config options listed for this class are options which may appear in each
53893  * individual column definition.
53894  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53895  * @constructor
53896  * @param {Object} config An Array of column config objects. See this class's
53897  * config objects for details.
53898 */
53899 Roo.grid.ColumnModel = function(config){
53900         /**
53901      * The config passed into the constructor
53902      */
53903     this.config = config;
53904     this.lookup = {};
53905
53906     // if no id, create one
53907     // if the column does not have a dataIndex mapping,
53908     // map it to the order it is in the config
53909     for(var i = 0, len = config.length; i < len; i++){
53910         var c = config[i];
53911         if(typeof c.dataIndex == "undefined"){
53912             c.dataIndex = i;
53913         }
53914         if(typeof c.renderer == "string"){
53915             c.renderer = Roo.util.Format[c.renderer];
53916         }
53917         if(typeof c.id == "undefined"){
53918             c.id = Roo.id();
53919         }
53920         if(c.editor && c.editor.xtype){
53921             c.editor  = Roo.factory(c.editor, Roo.grid);
53922         }
53923         if(c.editor && c.editor.isFormField){
53924             c.editor = new Roo.grid.GridEditor(c.editor);
53925         }
53926         this.lookup[c.id] = c;
53927     }
53928
53929     /**
53930      * The width of columns which have no width specified (defaults to 100)
53931      * @type Number
53932      */
53933     this.defaultWidth = 100;
53934
53935     /**
53936      * Default sortable of columns which have no sortable specified (defaults to false)
53937      * @type Boolean
53938      */
53939     this.defaultSortable = false;
53940
53941     this.addEvents({
53942         /**
53943              * @event widthchange
53944              * Fires when the width of a column changes.
53945              * @param {ColumnModel} this
53946              * @param {Number} columnIndex The column index
53947              * @param {Number} newWidth The new width
53948              */
53949             "widthchange": true,
53950         /**
53951              * @event headerchange
53952              * Fires when the text of a header changes.
53953              * @param {ColumnModel} this
53954              * @param {Number} columnIndex The column index
53955              * @param {Number} newText The new header text
53956              */
53957             "headerchange": true,
53958         /**
53959              * @event hiddenchange
53960              * Fires when a column is hidden or "unhidden".
53961              * @param {ColumnModel} this
53962              * @param {Number} columnIndex The column index
53963              * @param {Boolean} hidden true if hidden, false otherwise
53964              */
53965             "hiddenchange": true,
53966             /**
53967          * @event columnmoved
53968          * Fires when a column is moved.
53969          * @param {ColumnModel} this
53970          * @param {Number} oldIndex
53971          * @param {Number} newIndex
53972          */
53973         "columnmoved" : true,
53974         /**
53975          * @event columlockchange
53976          * Fires when a column's locked state is changed
53977          * @param {ColumnModel} this
53978          * @param {Number} colIndex
53979          * @param {Boolean} locked true if locked
53980          */
53981         "columnlockchange" : true
53982     });
53983     Roo.grid.ColumnModel.superclass.constructor.call(this);
53984 };
53985 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
53986     /**
53987      * @cfg {String} header The header text to display in the Grid view.
53988      */
53989     /**
53990      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
53991      * {@link Roo.data.Record} definition from which to draw the column's value. If not
53992      * specified, the column's index is used as an index into the Record's data Array.
53993      */
53994     /**
53995      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
53996      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
53997      */
53998     /**
53999      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54000      * Defaults to the value of the {@link #defaultSortable} property.
54001      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54002      */
54003     /**
54004      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54005      */
54006     /**
54007      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54008      */
54009     /**
54010      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54011      */
54012     /**
54013      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54014      */
54015     /**
54016      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54017      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54018      * default renderer uses the raw data value.
54019      */
54020        /**
54021      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54022      */
54023     /**
54024      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54025      */
54026
54027     /**
54028      * Returns the id of the column at the specified index.
54029      * @param {Number} index The column index
54030      * @return {String} the id
54031      */
54032     getColumnId : function(index){
54033         return this.config[index].id;
54034     },
54035
54036     /**
54037      * Returns the column for a specified id.
54038      * @param {String} id The column id
54039      * @return {Object} the column
54040      */
54041     getColumnById : function(id){
54042         return this.lookup[id];
54043     },
54044
54045     
54046     /**
54047      * Returns the column for a specified dataIndex.
54048      * @param {String} dataIndex The column dataIndex
54049      * @return {Object|Boolean} the column or false if not found
54050      */
54051     getColumnByDataIndex: function(dataIndex){
54052         var index = this.findColumnIndex(dataIndex);
54053         return index > -1 ? this.config[index] : false;
54054     },
54055     
54056     /**
54057      * Returns the index for a specified column id.
54058      * @param {String} id The column id
54059      * @return {Number} the index, or -1 if not found
54060      */
54061     getIndexById : function(id){
54062         for(var i = 0, len = this.config.length; i < len; i++){
54063             if(this.config[i].id == id){
54064                 return i;
54065             }
54066         }
54067         return -1;
54068     },
54069     
54070     /**
54071      * Returns the index for a specified column dataIndex.
54072      * @param {String} dataIndex The column dataIndex
54073      * @return {Number} the index, or -1 if not found
54074      */
54075     
54076     findColumnIndex : function(dataIndex){
54077         for(var i = 0, len = this.config.length; i < len; i++){
54078             if(this.config[i].dataIndex == dataIndex){
54079                 return i;
54080             }
54081         }
54082         return -1;
54083     },
54084     
54085     
54086     moveColumn : function(oldIndex, newIndex){
54087         var c = this.config[oldIndex];
54088         this.config.splice(oldIndex, 1);
54089         this.config.splice(newIndex, 0, c);
54090         this.dataMap = null;
54091         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54092     },
54093
54094     isLocked : function(colIndex){
54095         return this.config[colIndex].locked === true;
54096     },
54097
54098     setLocked : function(colIndex, value, suppressEvent){
54099         if(this.isLocked(colIndex) == value){
54100             return;
54101         }
54102         this.config[colIndex].locked = value;
54103         if(!suppressEvent){
54104             this.fireEvent("columnlockchange", this, colIndex, value);
54105         }
54106     },
54107
54108     getTotalLockedWidth : function(){
54109         var totalWidth = 0;
54110         for(var i = 0; i < this.config.length; i++){
54111             if(this.isLocked(i) && !this.isHidden(i)){
54112                 this.totalWidth += this.getColumnWidth(i);
54113             }
54114         }
54115         return totalWidth;
54116     },
54117
54118     getLockedCount : function(){
54119         for(var i = 0, len = this.config.length; i < len; i++){
54120             if(!this.isLocked(i)){
54121                 return i;
54122             }
54123         }
54124     },
54125
54126     /**
54127      * Returns the number of columns.
54128      * @return {Number}
54129      */
54130     getColumnCount : function(visibleOnly){
54131         if(visibleOnly === true){
54132             var c = 0;
54133             for(var i = 0, len = this.config.length; i < len; i++){
54134                 if(!this.isHidden(i)){
54135                     c++;
54136                 }
54137             }
54138             return c;
54139         }
54140         return this.config.length;
54141     },
54142
54143     /**
54144      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54145      * @param {Function} fn
54146      * @param {Object} scope (optional)
54147      * @return {Array} result
54148      */
54149     getColumnsBy : function(fn, scope){
54150         var r = [];
54151         for(var i = 0, len = this.config.length; i < len; i++){
54152             var c = this.config[i];
54153             if(fn.call(scope||this, c, i) === true){
54154                 r[r.length] = c;
54155             }
54156         }
54157         return r;
54158     },
54159
54160     /**
54161      * Returns true if the specified column is sortable.
54162      * @param {Number} col The column index
54163      * @return {Boolean}
54164      */
54165     isSortable : function(col){
54166         if(typeof this.config[col].sortable == "undefined"){
54167             return this.defaultSortable;
54168         }
54169         return this.config[col].sortable;
54170     },
54171
54172     /**
54173      * Returns the rendering (formatting) function defined for the column.
54174      * @param {Number} col The column index.
54175      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54176      */
54177     getRenderer : function(col){
54178         if(!this.config[col].renderer){
54179             return Roo.grid.ColumnModel.defaultRenderer;
54180         }
54181         return this.config[col].renderer;
54182     },
54183
54184     /**
54185      * Sets the rendering (formatting) function for a column.
54186      * @param {Number} col The column index
54187      * @param {Function} fn The function to use to process the cell's raw data
54188      * to return HTML markup for the grid view. The render function is called with
54189      * the following parameters:<ul>
54190      * <li>Data value.</li>
54191      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54192      * <li>css A CSS style string to apply to the table cell.</li>
54193      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54194      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54195      * <li>Row index</li>
54196      * <li>Column index</li>
54197      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54198      */
54199     setRenderer : function(col, fn){
54200         this.config[col].renderer = fn;
54201     },
54202
54203     /**
54204      * Returns the width for the specified column.
54205      * @param {Number} col The column index
54206      * @return {Number}
54207      */
54208     getColumnWidth : function(col){
54209         return this.config[col].width * 1 || this.defaultWidth;
54210     },
54211
54212     /**
54213      * Sets the width for a column.
54214      * @param {Number} col The column index
54215      * @param {Number} width The new width
54216      */
54217     setColumnWidth : function(col, width, suppressEvent){
54218         this.config[col].width = width;
54219         this.totalWidth = null;
54220         if(!suppressEvent){
54221              this.fireEvent("widthchange", this, col, width);
54222         }
54223     },
54224
54225     /**
54226      * Returns the total width of all columns.
54227      * @param {Boolean} includeHidden True to include hidden column widths
54228      * @return {Number}
54229      */
54230     getTotalWidth : function(includeHidden){
54231         if(!this.totalWidth){
54232             this.totalWidth = 0;
54233             for(var i = 0, len = this.config.length; i < len; i++){
54234                 if(includeHidden || !this.isHidden(i)){
54235                     this.totalWidth += this.getColumnWidth(i);
54236                 }
54237             }
54238         }
54239         return this.totalWidth;
54240     },
54241
54242     /**
54243      * Returns the header for the specified column.
54244      * @param {Number} col The column index
54245      * @return {String}
54246      */
54247     getColumnHeader : function(col){
54248         return this.config[col].header;
54249     },
54250
54251     /**
54252      * Sets the header for a column.
54253      * @param {Number} col The column index
54254      * @param {String} header The new header
54255      */
54256     setColumnHeader : function(col, header){
54257         this.config[col].header = header;
54258         this.fireEvent("headerchange", this, col, header);
54259     },
54260
54261     /**
54262      * Returns the tooltip for the specified column.
54263      * @param {Number} col The column index
54264      * @return {String}
54265      */
54266     getColumnTooltip : function(col){
54267             return this.config[col].tooltip;
54268     },
54269     /**
54270      * Sets the tooltip for a column.
54271      * @param {Number} col The column index
54272      * @param {String} tooltip The new tooltip
54273      */
54274     setColumnTooltip : function(col, tooltip){
54275             this.config[col].tooltip = tooltip;
54276     },
54277
54278     /**
54279      * Returns the dataIndex for the specified column.
54280      * @param {Number} col The column index
54281      * @return {Number}
54282      */
54283     getDataIndex : function(col){
54284         return this.config[col].dataIndex;
54285     },
54286
54287     /**
54288      * Sets the dataIndex for a column.
54289      * @param {Number} col The column index
54290      * @param {Number} dataIndex The new dataIndex
54291      */
54292     setDataIndex : function(col, dataIndex){
54293         this.config[col].dataIndex = dataIndex;
54294     },
54295
54296     
54297     
54298     /**
54299      * Returns true if the cell is editable.
54300      * @param {Number} colIndex The column index
54301      * @param {Number} rowIndex The row index
54302      * @return {Boolean}
54303      */
54304     isCellEditable : function(colIndex, rowIndex){
54305         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54306     },
54307
54308     /**
54309      * Returns the editor defined for the cell/column.
54310      * return false or null to disable editing.
54311      * @param {Number} colIndex The column index
54312      * @param {Number} rowIndex The row index
54313      * @return {Object}
54314      */
54315     getCellEditor : function(colIndex, rowIndex){
54316         return this.config[colIndex].editor;
54317     },
54318
54319     /**
54320      * Sets if a column is editable.
54321      * @param {Number} col The column index
54322      * @param {Boolean} editable True if the column is editable
54323      */
54324     setEditable : function(col, editable){
54325         this.config[col].editable = editable;
54326     },
54327
54328
54329     /**
54330      * Returns true if the column is hidden.
54331      * @param {Number} colIndex The column index
54332      * @return {Boolean}
54333      */
54334     isHidden : function(colIndex){
54335         return this.config[colIndex].hidden;
54336     },
54337
54338
54339     /**
54340      * Returns true if the column width cannot be changed
54341      */
54342     isFixed : function(colIndex){
54343         return this.config[colIndex].fixed;
54344     },
54345
54346     /**
54347      * Returns true if the column can be resized
54348      * @return {Boolean}
54349      */
54350     isResizable : function(colIndex){
54351         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54352     },
54353     /**
54354      * Sets if a column is hidden.
54355      * @param {Number} colIndex The column index
54356      * @param {Boolean} hidden True if the column is hidden
54357      */
54358     setHidden : function(colIndex, hidden){
54359         this.config[colIndex].hidden = hidden;
54360         this.totalWidth = null;
54361         this.fireEvent("hiddenchange", this, colIndex, hidden);
54362     },
54363
54364     /**
54365      * Sets the editor for a column.
54366      * @param {Number} col The column index
54367      * @param {Object} editor The editor object
54368      */
54369     setEditor : function(col, editor){
54370         this.config[col].editor = editor;
54371     }
54372 });
54373
54374 Roo.grid.ColumnModel.defaultRenderer = function(value){
54375         if(typeof value == "string" && value.length < 1){
54376             return "&#160;";
54377         }
54378         return value;
54379 };
54380
54381 // Alias for backwards compatibility
54382 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54383 /*
54384  * Based on:
54385  * Ext JS Library 1.1.1
54386  * Copyright(c) 2006-2007, Ext JS, LLC.
54387  *
54388  * Originally Released Under LGPL - original licence link has changed is not relivant.
54389  *
54390  * Fork - LGPL
54391  * <script type="text/javascript">
54392  */
54393
54394 /**
54395  * @class Roo.grid.AbstractSelectionModel
54396  * @extends Roo.util.Observable
54397  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54398  * implemented by descendant classes.  This class should not be directly instantiated.
54399  * @constructor
54400  */
54401 Roo.grid.AbstractSelectionModel = function(){
54402     this.locked = false;
54403     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54404 };
54405
54406 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54407     /** @ignore Called by the grid automatically. Do not call directly. */
54408     init : function(grid){
54409         this.grid = grid;
54410         this.initEvents();
54411     },
54412
54413     /**
54414      * Locks the selections.
54415      */
54416     lock : function(){
54417         this.locked = true;
54418     },
54419
54420     /**
54421      * Unlocks the selections.
54422      */
54423     unlock : function(){
54424         this.locked = false;
54425     },
54426
54427     /**
54428      * Returns true if the selections are locked.
54429      * @return {Boolean}
54430      */
54431     isLocked : function(){
54432         return this.locked;
54433     }
54434 });/*
54435  * Based on:
54436  * Ext JS Library 1.1.1
54437  * Copyright(c) 2006-2007, Ext JS, LLC.
54438  *
54439  * Originally Released Under LGPL - original licence link has changed is not relivant.
54440  *
54441  * Fork - LGPL
54442  * <script type="text/javascript">
54443  */
54444 /**
54445  * @extends Roo.grid.AbstractSelectionModel
54446  * @class Roo.grid.RowSelectionModel
54447  * The default SelectionModel used by {@link Roo.grid.Grid}.
54448  * It supports multiple selections and keyboard selection/navigation. 
54449  * @constructor
54450  * @param {Object} config
54451  */
54452 Roo.grid.RowSelectionModel = function(config){
54453     Roo.apply(this, config);
54454     this.selections = new Roo.util.MixedCollection(false, function(o){
54455         return o.id;
54456     });
54457
54458     this.last = false;
54459     this.lastActive = false;
54460
54461     this.addEvents({
54462         /**
54463              * @event selectionchange
54464              * Fires when the selection changes
54465              * @param {SelectionModel} this
54466              */
54467             "selectionchange" : true,
54468         /**
54469              * @event afterselectionchange
54470              * Fires after the selection changes (eg. by key press or clicking)
54471              * @param {SelectionModel} this
54472              */
54473             "afterselectionchange" : true,
54474         /**
54475              * @event beforerowselect
54476              * Fires when a row is selected being selected, return false to cancel.
54477              * @param {SelectionModel} this
54478              * @param {Number} rowIndex The selected index
54479              * @param {Boolean} keepExisting False if other selections will be cleared
54480              */
54481             "beforerowselect" : true,
54482         /**
54483              * @event rowselect
54484              * Fires when a row is selected.
54485              * @param {SelectionModel} this
54486              * @param {Number} rowIndex The selected index
54487              * @param {Roo.data.Record} r The record
54488              */
54489             "rowselect" : true,
54490         /**
54491              * @event rowdeselect
54492              * Fires when a row is deselected.
54493              * @param {SelectionModel} this
54494              * @param {Number} rowIndex The selected index
54495              */
54496         "rowdeselect" : true
54497     });
54498     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54499     this.locked = false;
54500 };
54501
54502 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54503     /**
54504      * @cfg {Boolean} singleSelect
54505      * True to allow selection of only one row at a time (defaults to false)
54506      */
54507     singleSelect : false,
54508
54509     // private
54510     initEvents : function(){
54511
54512         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54513             this.grid.on("mousedown", this.handleMouseDown, this);
54514         }else{ // allow click to work like normal
54515             this.grid.on("rowclick", this.handleDragableRowClick, this);
54516         }
54517
54518         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54519             "up" : function(e){
54520                 if(!e.shiftKey){
54521                     this.selectPrevious(e.shiftKey);
54522                 }else if(this.last !== false && this.lastActive !== false){
54523                     var last = this.last;
54524                     this.selectRange(this.last,  this.lastActive-1);
54525                     this.grid.getView().focusRow(this.lastActive);
54526                     if(last !== false){
54527                         this.last = last;
54528                     }
54529                 }else{
54530                     this.selectFirstRow();
54531                 }
54532                 this.fireEvent("afterselectionchange", this);
54533             },
54534             "down" : function(e){
54535                 if(!e.shiftKey){
54536                     this.selectNext(e.shiftKey);
54537                 }else if(this.last !== false && this.lastActive !== false){
54538                     var last = this.last;
54539                     this.selectRange(this.last,  this.lastActive+1);
54540                     this.grid.getView().focusRow(this.lastActive);
54541                     if(last !== false){
54542                         this.last = last;
54543                     }
54544                 }else{
54545                     this.selectFirstRow();
54546                 }
54547                 this.fireEvent("afterselectionchange", this);
54548             },
54549             scope: this
54550         });
54551
54552         var view = this.grid.view;
54553         view.on("refresh", this.onRefresh, this);
54554         view.on("rowupdated", this.onRowUpdated, this);
54555         view.on("rowremoved", this.onRemove, this);
54556     },
54557
54558     // private
54559     onRefresh : function(){
54560         var ds = this.grid.dataSource, i, v = this.grid.view;
54561         var s = this.selections;
54562         s.each(function(r){
54563             if((i = ds.indexOfId(r.id)) != -1){
54564                 v.onRowSelect(i);
54565             }else{
54566                 s.remove(r);
54567             }
54568         });
54569     },
54570
54571     // private
54572     onRemove : function(v, index, r){
54573         this.selections.remove(r);
54574     },
54575
54576     // private
54577     onRowUpdated : function(v, index, r){
54578         if(this.isSelected(r)){
54579             v.onRowSelect(index);
54580         }
54581     },
54582
54583     /**
54584      * Select records.
54585      * @param {Array} records The records to select
54586      * @param {Boolean} keepExisting (optional) True to keep existing selections
54587      */
54588     selectRecords : function(records, keepExisting){
54589         if(!keepExisting){
54590             this.clearSelections();
54591         }
54592         var ds = this.grid.dataSource;
54593         for(var i = 0, len = records.length; i < len; i++){
54594             this.selectRow(ds.indexOf(records[i]), true);
54595         }
54596     },
54597
54598     /**
54599      * Gets the number of selected rows.
54600      * @return {Number}
54601      */
54602     getCount : function(){
54603         return this.selections.length;
54604     },
54605
54606     /**
54607      * Selects the first row in the grid.
54608      */
54609     selectFirstRow : function(){
54610         this.selectRow(0);
54611     },
54612
54613     /**
54614      * Select the last row.
54615      * @param {Boolean} keepExisting (optional) True to keep existing selections
54616      */
54617     selectLastRow : function(keepExisting){
54618         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54619     },
54620
54621     /**
54622      * Selects the row immediately following the last selected row.
54623      * @param {Boolean} keepExisting (optional) True to keep existing selections
54624      */
54625     selectNext : function(keepExisting){
54626         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54627             this.selectRow(this.last+1, keepExisting);
54628             this.grid.getView().focusRow(this.last);
54629         }
54630     },
54631
54632     /**
54633      * Selects the row that precedes the last selected row.
54634      * @param {Boolean} keepExisting (optional) True to keep existing selections
54635      */
54636     selectPrevious : function(keepExisting){
54637         if(this.last){
54638             this.selectRow(this.last-1, keepExisting);
54639             this.grid.getView().focusRow(this.last);
54640         }
54641     },
54642
54643     /**
54644      * Returns the selected records
54645      * @return {Array} Array of selected records
54646      */
54647     getSelections : function(){
54648         return [].concat(this.selections.items);
54649     },
54650
54651     /**
54652      * Returns the first selected record.
54653      * @return {Record}
54654      */
54655     getSelected : function(){
54656         return this.selections.itemAt(0);
54657     },
54658
54659
54660     /**
54661      * Clears all selections.
54662      */
54663     clearSelections : function(fast){
54664         if(this.locked) return;
54665         if(fast !== true){
54666             var ds = this.grid.dataSource;
54667             var s = this.selections;
54668             s.each(function(r){
54669                 this.deselectRow(ds.indexOfId(r.id));
54670             }, this);
54671             s.clear();
54672         }else{
54673             this.selections.clear();
54674         }
54675         this.last = false;
54676     },
54677
54678
54679     /**
54680      * Selects all rows.
54681      */
54682     selectAll : function(){
54683         if(this.locked) return;
54684         this.selections.clear();
54685         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54686             this.selectRow(i, true);
54687         }
54688     },
54689
54690     /**
54691      * Returns True if there is a selection.
54692      * @return {Boolean}
54693      */
54694     hasSelection : function(){
54695         return this.selections.length > 0;
54696     },
54697
54698     /**
54699      * Returns True if the specified row is selected.
54700      * @param {Number/Record} record The record or index of the record to check
54701      * @return {Boolean}
54702      */
54703     isSelected : function(index){
54704         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54705         return (r && this.selections.key(r.id) ? true : false);
54706     },
54707
54708     /**
54709      * Returns True if the specified record id is selected.
54710      * @param {String} id The id of record to check
54711      * @return {Boolean}
54712      */
54713     isIdSelected : function(id){
54714         return (this.selections.key(id) ? true : false);
54715     },
54716
54717     // private
54718     handleMouseDown : function(e, t){
54719         var view = this.grid.getView(), rowIndex;
54720         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54721             return;
54722         };
54723         if(e.shiftKey && this.last !== false){
54724             var last = this.last;
54725             this.selectRange(last, rowIndex, e.ctrlKey);
54726             this.last = last; // reset the last
54727             view.focusRow(rowIndex);
54728         }else{
54729             var isSelected = this.isSelected(rowIndex);
54730             if(e.button !== 0 && isSelected){
54731                 view.focusRow(rowIndex);
54732             }else if(e.ctrlKey && isSelected){
54733                 this.deselectRow(rowIndex);
54734             }else if(!isSelected){
54735                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54736                 view.focusRow(rowIndex);
54737             }
54738         }
54739         this.fireEvent("afterselectionchange", this);
54740     },
54741     // private
54742     handleDragableRowClick :  function(grid, rowIndex, e) 
54743     {
54744         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54745             this.selectRow(rowIndex, false);
54746             grid.view.focusRow(rowIndex);
54747              this.fireEvent("afterselectionchange", this);
54748         }
54749     },
54750     
54751     /**
54752      * Selects multiple rows.
54753      * @param {Array} rows Array of the indexes of the row to select
54754      * @param {Boolean} keepExisting (optional) True to keep existing selections
54755      */
54756     selectRows : function(rows, keepExisting){
54757         if(!keepExisting){
54758             this.clearSelections();
54759         }
54760         for(var i = 0, len = rows.length; i < len; i++){
54761             this.selectRow(rows[i], true);
54762         }
54763     },
54764
54765     /**
54766      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54767      * @param {Number} startRow The index of the first row in the range
54768      * @param {Number} endRow The index of the last row in the range
54769      * @param {Boolean} keepExisting (optional) True to retain existing selections
54770      */
54771     selectRange : function(startRow, endRow, keepExisting){
54772         if(this.locked) return;
54773         if(!keepExisting){
54774             this.clearSelections();
54775         }
54776         if(startRow <= endRow){
54777             for(var i = startRow; i <= endRow; i++){
54778                 this.selectRow(i, true);
54779             }
54780         }else{
54781             for(var i = startRow; i >= endRow; i--){
54782                 this.selectRow(i, true);
54783             }
54784         }
54785     },
54786
54787     /**
54788      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54789      * @param {Number} startRow The index of the first row in the range
54790      * @param {Number} endRow The index of the last row in the range
54791      */
54792     deselectRange : function(startRow, endRow, preventViewNotify){
54793         if(this.locked) return;
54794         for(var i = startRow; i <= endRow; i++){
54795             this.deselectRow(i, preventViewNotify);
54796         }
54797     },
54798
54799     /**
54800      * Selects a row.
54801      * @param {Number} row The index of the row to select
54802      * @param {Boolean} keepExisting (optional) True to keep existing selections
54803      */
54804     selectRow : function(index, keepExisting, preventViewNotify){
54805         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54806         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54807             if(!keepExisting || this.singleSelect){
54808                 this.clearSelections();
54809             }
54810             var r = this.grid.dataSource.getAt(index);
54811             this.selections.add(r);
54812             this.last = this.lastActive = index;
54813             if(!preventViewNotify){
54814                 this.grid.getView().onRowSelect(index);
54815             }
54816             this.fireEvent("rowselect", this, index, r);
54817             this.fireEvent("selectionchange", this);
54818         }
54819     },
54820
54821     /**
54822      * Deselects a row.
54823      * @param {Number} row The index of the row to deselect
54824      */
54825     deselectRow : function(index, preventViewNotify){
54826         if(this.locked) return;
54827         if(this.last == index){
54828             this.last = false;
54829         }
54830         if(this.lastActive == index){
54831             this.lastActive = false;
54832         }
54833         var r = this.grid.dataSource.getAt(index);
54834         this.selections.remove(r);
54835         if(!preventViewNotify){
54836             this.grid.getView().onRowDeselect(index);
54837         }
54838         this.fireEvent("rowdeselect", this, index);
54839         this.fireEvent("selectionchange", this);
54840     },
54841
54842     // private
54843     restoreLast : function(){
54844         if(this._last){
54845             this.last = this._last;
54846         }
54847     },
54848
54849     // private
54850     acceptsNav : function(row, col, cm){
54851         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54852     },
54853
54854     // private
54855     onEditorKey : function(field, e){
54856         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54857         if(k == e.TAB){
54858             e.stopEvent();
54859             ed.completeEdit();
54860             if(e.shiftKey){
54861                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54862             }else{
54863                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54864             }
54865         }else if(k == e.ENTER && !e.ctrlKey){
54866             e.stopEvent();
54867             ed.completeEdit();
54868             if(e.shiftKey){
54869                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54870             }else{
54871                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54872             }
54873         }else if(k == e.ESC){
54874             ed.cancelEdit();
54875         }
54876         if(newCell){
54877             g.startEditing(newCell[0], newCell[1]);
54878         }
54879     }
54880 });/*
54881  * Based on:
54882  * Ext JS Library 1.1.1
54883  * Copyright(c) 2006-2007, Ext JS, LLC.
54884  *
54885  * Originally Released Under LGPL - original licence link has changed is not relivant.
54886  *
54887  * Fork - LGPL
54888  * <script type="text/javascript">
54889  */
54890 /**
54891  * @class Roo.grid.CellSelectionModel
54892  * @extends Roo.grid.AbstractSelectionModel
54893  * This class provides the basic implementation for cell selection in a grid.
54894  * @constructor
54895  * @param {Object} config The object containing the configuration of this model.
54896  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54897  */
54898 Roo.grid.CellSelectionModel = function(config){
54899     Roo.apply(this, config);
54900
54901     this.selection = null;
54902
54903     this.addEvents({
54904         /**
54905              * @event beforerowselect
54906              * Fires before a cell is selected.
54907              * @param {SelectionModel} this
54908              * @param {Number} rowIndex The selected row index
54909              * @param {Number} colIndex The selected cell index
54910              */
54911             "beforecellselect" : true,
54912         /**
54913              * @event cellselect
54914              * Fires when a cell is selected.
54915              * @param {SelectionModel} this
54916              * @param {Number} rowIndex The selected row index
54917              * @param {Number} colIndex The selected cell index
54918              */
54919             "cellselect" : true,
54920         /**
54921              * @event selectionchange
54922              * Fires when the active selection changes.
54923              * @param {SelectionModel} this
54924              * @param {Object} selection null for no selection or an object (o) with two properties
54925                 <ul>
54926                 <li>o.record: the record object for the row the selection is in</li>
54927                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54928                 </ul>
54929              */
54930             "selectionchange" : true,
54931         /**
54932              * @event tabend
54933              * Fires when the tab (or enter) was pressed on the last editable cell
54934              * You can use this to trigger add new row.
54935              * @param {SelectionModel} this
54936              */
54937             "tabend" : true,
54938          /**
54939              * @event beforeeditnext
54940              * Fires before the next editable sell is made active
54941              * You can use this to skip to another cell or fire the tabend
54942              *    if you set cell to false
54943              * @param {Object} eventdata object : { cell : [ row, col ] } 
54944              */
54945             "beforeeditnext" : true
54946     });
54947     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54948 };
54949
54950 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54951     
54952     enter_is_tab: false,
54953
54954     /** @ignore */
54955     initEvents : function(){
54956         this.grid.on("mousedown", this.handleMouseDown, this);
54957         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54958         var view = this.grid.view;
54959         view.on("refresh", this.onViewChange, this);
54960         view.on("rowupdated", this.onRowUpdated, this);
54961         view.on("beforerowremoved", this.clearSelections, this);
54962         view.on("beforerowsinserted", this.clearSelections, this);
54963         if(this.grid.isEditor){
54964             this.grid.on("beforeedit", this.beforeEdit,  this);
54965         }
54966     },
54967
54968         //private
54969     beforeEdit : function(e){
54970         this.select(e.row, e.column, false, true, e.record);
54971     },
54972
54973         //private
54974     onRowUpdated : function(v, index, r){
54975         if(this.selection && this.selection.record == r){
54976             v.onCellSelect(index, this.selection.cell[1]);
54977         }
54978     },
54979
54980         //private
54981     onViewChange : function(){
54982         this.clearSelections(true);
54983     },
54984
54985         /**
54986          * Returns the currently selected cell,.
54987          * @return {Array} The selected cell (row, column) or null if none selected.
54988          */
54989     getSelectedCell : function(){
54990         return this.selection ? this.selection.cell : null;
54991     },
54992
54993     /**
54994      * Clears all selections.
54995      * @param {Boolean} true to prevent the gridview from being notified about the change.
54996      */
54997     clearSelections : function(preventNotify){
54998         var s = this.selection;
54999         if(s){
55000             if(preventNotify !== true){
55001                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55002             }
55003             this.selection = null;
55004             this.fireEvent("selectionchange", this, null);
55005         }
55006     },
55007
55008     /**
55009      * Returns true if there is a selection.
55010      * @return {Boolean}
55011      */
55012     hasSelection : function(){
55013         return this.selection ? true : false;
55014     },
55015
55016     /** @ignore */
55017     handleMouseDown : function(e, t){
55018         var v = this.grid.getView();
55019         if(this.isLocked()){
55020             return;
55021         };
55022         var row = v.findRowIndex(t);
55023         var cell = v.findCellIndex(t);
55024         if(row !== false && cell !== false){
55025             this.select(row, cell);
55026         }
55027     },
55028
55029     /**
55030      * Selects a cell.
55031      * @param {Number} rowIndex
55032      * @param {Number} collIndex
55033      */
55034     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55035         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55036             this.clearSelections();
55037             r = r || this.grid.dataSource.getAt(rowIndex);
55038             this.selection = {
55039                 record : r,
55040                 cell : [rowIndex, colIndex]
55041             };
55042             if(!preventViewNotify){
55043                 var v = this.grid.getView();
55044                 v.onCellSelect(rowIndex, colIndex);
55045                 if(preventFocus !== true){
55046                     v.focusCell(rowIndex, colIndex);
55047                 }
55048             }
55049             this.fireEvent("cellselect", this, rowIndex, colIndex);
55050             this.fireEvent("selectionchange", this, this.selection);
55051         }
55052     },
55053
55054         //private
55055     isSelectable : function(rowIndex, colIndex, cm){
55056         return !cm.isHidden(colIndex);
55057     },
55058
55059     /** @ignore */
55060     handleKeyDown : function(e){
55061         //Roo.log('Cell Sel Model handleKeyDown');
55062         if(!e.isNavKeyPress()){
55063             return;
55064         }
55065         var g = this.grid, s = this.selection;
55066         if(!s){
55067             e.stopEvent();
55068             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55069             if(cell){
55070                 this.select(cell[0], cell[1]);
55071             }
55072             return;
55073         }
55074         var sm = this;
55075         var walk = function(row, col, step){
55076             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55077         };
55078         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55079         var newCell;
55080
55081       
55082
55083         switch(k){
55084             case e.TAB:
55085                 // handled by onEditorKey
55086                 if (g.isEditor && g.editing) {
55087                     return;
55088                 }
55089                 if(e.shiftKey) {
55090                     newCell = walk(r, c-1, -1);
55091                 } else {
55092                     newCell = walk(r, c+1, 1);
55093                 }
55094                 break;
55095             
55096             case e.DOWN:
55097                newCell = walk(r+1, c, 1);
55098                 break;
55099             
55100             case e.UP:
55101                 newCell = walk(r-1, c, -1);
55102                 break;
55103             
55104             case e.RIGHT:
55105                 newCell = walk(r, c+1, 1);
55106                 break;
55107             
55108             case e.LEFT:
55109                 newCell = walk(r, c-1, -1);
55110                 break;
55111             
55112             case e.ENTER:
55113                 
55114                 if(g.isEditor && !g.editing){
55115                    g.startEditing(r, c);
55116                    e.stopEvent();
55117                    return;
55118                 }
55119                 
55120                 
55121              break;
55122         };
55123         if(newCell){
55124             this.select(newCell[0], newCell[1]);
55125             e.stopEvent();
55126             
55127         }
55128     },
55129
55130     acceptsNav : function(row, col, cm){
55131         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55132     },
55133     /**
55134      * Selects a cell.
55135      * @param {Number} field (not used) - as it's normally used as a listener
55136      * @param {Number} e - event - fake it by using
55137      *
55138      * var e = Roo.EventObjectImpl.prototype;
55139      * e.keyCode = e.TAB
55140      *
55141      * 
55142      */
55143     onEditorKey : function(field, e){
55144         
55145         var k = e.getKey(),
55146             newCell,
55147             g = this.grid,
55148             ed = g.activeEditor,
55149             forward = false;
55150         ///Roo.log('onEditorKey' + k);
55151         
55152         
55153         if (this.enter_is_tab && k == e.ENTER) {
55154             k = e.TAB;
55155         }
55156         
55157         if(k == e.TAB){
55158             if(e.shiftKey){
55159                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55160             }else{
55161                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55162                 forward = true;
55163             }
55164             
55165             e.stopEvent();
55166             
55167         } else if(k == e.ENTER &&  !e.ctrlKey){
55168             ed.completeEdit();
55169             e.stopEvent();
55170             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55171         
55172                 } else if(k == e.ESC){
55173             ed.cancelEdit();
55174         }
55175                 
55176         if (newCell) {
55177             var ecall = { cell : newCell, forward : forward };
55178             this.fireEvent('beforeeditnext', ecall );
55179             newCell = ecall.cell;
55180                         forward = ecall.forward;
55181         }
55182                 
55183         if(newCell){
55184             //Roo.log('next cell after edit');
55185             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55186         } else if (forward) {
55187             // tabbed past last
55188             this.fireEvent.defer(100, this, ['tabend',this]);
55189         }
55190     }
55191 });/*
55192  * Based on:
55193  * Ext JS Library 1.1.1
55194  * Copyright(c) 2006-2007, Ext JS, LLC.
55195  *
55196  * Originally Released Under LGPL - original licence link has changed is not relivant.
55197  *
55198  * Fork - LGPL
55199  * <script type="text/javascript">
55200  */
55201  
55202 /**
55203  * @class Roo.grid.EditorGrid
55204  * @extends Roo.grid.Grid
55205  * Class for creating and editable grid.
55206  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55207  * The container MUST have some type of size defined for the grid to fill. The container will be 
55208  * automatically set to position relative if it isn't already.
55209  * @param {Object} dataSource The data model to bind to
55210  * @param {Object} colModel The column model with info about this grid's columns
55211  */
55212 Roo.grid.EditorGrid = function(container, config){
55213     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55214     this.getGridEl().addClass("xedit-grid");
55215
55216     if(!this.selModel){
55217         this.selModel = new Roo.grid.CellSelectionModel();
55218     }
55219
55220     this.activeEditor = null;
55221
55222         this.addEvents({
55223             /**
55224              * @event beforeedit
55225              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55226              * <ul style="padding:5px;padding-left:16px;">
55227              * <li>grid - This grid</li>
55228              * <li>record - The record being edited</li>
55229              * <li>field - The field name being edited</li>
55230              * <li>value - The value for the field being edited.</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             "beforeedit" : true,
55238             /**
55239              * @event afteredit
55240              * Fires after a cell is edited. <br />
55241              * <ul style="padding:5px;padding-left:16px;">
55242              * <li>grid - This grid</li>
55243              * <li>record - The record being edited</li>
55244              * <li>field - The field name being edited</li>
55245              * <li>value - The value being set</li>
55246              * <li>originalValue - The original value for the field, before the edit.</li>
55247              * <li>row - The grid row index</li>
55248              * <li>column - The grid column index</li>
55249              * </ul>
55250              * @param {Object} e An edit event (see above for description)
55251              */
55252             "afteredit" : true,
55253             /**
55254              * @event validateedit
55255              * Fires after a cell is edited, but before the value is set in the record. 
55256          * You can use this to modify the value being set in the field, Return false
55257              * to cancel the change. The edit event object has the following properties <br />
55258              * <ul style="padding:5px;padding-left:16px;">
55259          * <li>editor - This editor</li>
55260              * <li>grid - This grid</li>
55261              * <li>record - The record being edited</li>
55262              * <li>field - The field name being edited</li>
55263              * <li>value - The value being set</li>
55264              * <li>originalValue - The original value for the field, before the edit.</li>
55265              * <li>row - The grid row index</li>
55266              * <li>column - The grid column index</li>
55267              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55268              * </ul>
55269              * @param {Object} e An edit event (see above for description)
55270              */
55271             "validateedit" : true
55272         });
55273     this.on("bodyscroll", this.stopEditing,  this);
55274     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55275 };
55276
55277 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55278     /**
55279      * @cfg {Number} clicksToEdit
55280      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55281      */
55282     clicksToEdit: 2,
55283
55284     // private
55285     isEditor : true,
55286     // private
55287     trackMouseOver: false, // causes very odd FF errors
55288
55289     onCellDblClick : function(g, row, col){
55290         this.startEditing(row, col);
55291     },
55292
55293     onEditComplete : function(ed, value, startValue){
55294         this.editing = false;
55295         this.activeEditor = null;
55296         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55297         var r = ed.record;
55298         var field = this.colModel.getDataIndex(ed.col);
55299         var e = {
55300             grid: this,
55301             record: r,
55302             field: field,
55303             originalValue: startValue,
55304             value: value,
55305             row: ed.row,
55306             column: ed.col,
55307             cancel:false,
55308             editor: ed
55309         };
55310         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55311         cell.show();
55312           
55313         if(String(value) !== String(startValue)){
55314             
55315             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55316                 r.set(field, e.value);
55317                 // if we are dealing with a combo box..
55318                 // then we also set the 'name' colum to be the displayField
55319                 if (ed.field.displayField && ed.field.name) {
55320                     r.set(ed.field.name, ed.field.el.dom.value);
55321                 }
55322                 
55323                 delete e.cancel; //?? why!!!
55324                 this.fireEvent("afteredit", e);
55325             }
55326         } else {
55327             this.fireEvent("afteredit", e); // always fire it!
55328         }
55329         this.view.focusCell(ed.row, ed.col);
55330     },
55331
55332     /**
55333      * Starts editing the specified for the specified row/column
55334      * @param {Number} rowIndex
55335      * @param {Number} colIndex
55336      */
55337     startEditing : function(row, col){
55338         this.stopEditing();
55339         if(this.colModel.isCellEditable(col, row)){
55340             this.view.ensureVisible(row, col, true);
55341           
55342             var r = this.dataSource.getAt(row);
55343             var field = this.colModel.getDataIndex(col);
55344             var cell = Roo.get(this.view.getCell(row,col));
55345             var e = {
55346                 grid: this,
55347                 record: r,
55348                 field: field,
55349                 value: r.data[field],
55350                 row: row,
55351                 column: col,
55352                 cancel:false 
55353             };
55354             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55355                 this.editing = true;
55356                 var ed = this.colModel.getCellEditor(col, row);
55357                 
55358                 if (!ed) {
55359                     return;
55360                 }
55361                 if(!ed.rendered){
55362                     ed.render(ed.parentEl || document.body);
55363                 }
55364                 ed.field.reset();
55365                
55366                 cell.hide();
55367                 
55368                 (function(){ // complex but required for focus issues in safari, ie and opera
55369                     ed.row = row;
55370                     ed.col = col;
55371                     ed.record = r;
55372                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55373                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55374                     this.activeEditor = ed;
55375                     var v = r.data[field];
55376                     ed.startEdit(this.view.getCell(row, col), v);
55377                     // combo's with 'displayField and name set
55378                     if (ed.field.displayField && ed.field.name) {
55379                         ed.field.el.dom.value = r.data[ed.field.name];
55380                     }
55381                     
55382                     
55383                 }).defer(50, this);
55384             }
55385         }
55386     },
55387         
55388     /**
55389      * Stops any active editing
55390      */
55391     stopEditing : function(){
55392         if(this.activeEditor){
55393             this.activeEditor.completeEdit();
55394         }
55395         this.activeEditor = null;
55396     },
55397         
55398          /**
55399      * Called to get grid's drag proxy text, by default returns this.ddText.
55400      * @return {String}
55401      */
55402     getDragDropText : function(){
55403         var count = this.selModel.getSelectedCell() ? 1 : 0;
55404         return String.format(this.ddText, count, count == 1 ? '' : 's');
55405     }
55406         
55407 });/*
55408  * Based on:
55409  * Ext JS Library 1.1.1
55410  * Copyright(c) 2006-2007, Ext JS, LLC.
55411  *
55412  * Originally Released Under LGPL - original licence link has changed is not relivant.
55413  *
55414  * Fork - LGPL
55415  * <script type="text/javascript">
55416  */
55417
55418 // private - not really -- you end up using it !
55419 // This is a support class used internally by the Grid components
55420
55421 /**
55422  * @class Roo.grid.GridEditor
55423  * @extends Roo.Editor
55424  * Class for creating and editable grid elements.
55425  * @param {Object} config any settings (must include field)
55426  */
55427 Roo.grid.GridEditor = function(field, config){
55428     if (!config && field.field) {
55429         config = field;
55430         field = Roo.factory(config.field, Roo.form);
55431     }
55432     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55433     field.monitorTab = false;
55434 };
55435
55436 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55437     
55438     /**
55439      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55440      */
55441     
55442     alignment: "tl-tl",
55443     autoSize: "width",
55444     hideEl : false,
55445     cls: "x-small-editor x-grid-editor",
55446     shim:false,
55447     shadow:"frame"
55448 });/*
55449  * Based on:
55450  * Ext JS Library 1.1.1
55451  * Copyright(c) 2006-2007, Ext JS, LLC.
55452  *
55453  * Originally Released Under LGPL - original licence link has changed is not relivant.
55454  *
55455  * Fork - LGPL
55456  * <script type="text/javascript">
55457  */
55458   
55459
55460   
55461 Roo.grid.PropertyRecord = Roo.data.Record.create([
55462     {name:'name',type:'string'},  'value'
55463 ]);
55464
55465
55466 Roo.grid.PropertyStore = function(grid, source){
55467     this.grid = grid;
55468     this.store = new Roo.data.Store({
55469         recordType : Roo.grid.PropertyRecord
55470     });
55471     this.store.on('update', this.onUpdate,  this);
55472     if(source){
55473         this.setSource(source);
55474     }
55475     Roo.grid.PropertyStore.superclass.constructor.call(this);
55476 };
55477
55478
55479
55480 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55481     setSource : function(o){
55482         this.source = o;
55483         this.store.removeAll();
55484         var data = [];
55485         for(var k in o){
55486             if(this.isEditableValue(o[k])){
55487                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55488             }
55489         }
55490         this.store.loadRecords({records: data}, {}, true);
55491     },
55492
55493     onUpdate : function(ds, record, type){
55494         if(type == Roo.data.Record.EDIT){
55495             var v = record.data['value'];
55496             var oldValue = record.modified['value'];
55497             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55498                 this.source[record.id] = v;
55499                 record.commit();
55500                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55501             }else{
55502                 record.reject();
55503             }
55504         }
55505     },
55506
55507     getProperty : function(row){
55508        return this.store.getAt(row);
55509     },
55510
55511     isEditableValue: function(val){
55512         if(val && val instanceof Date){
55513             return true;
55514         }else if(typeof val == 'object' || typeof val == 'function'){
55515             return false;
55516         }
55517         return true;
55518     },
55519
55520     setValue : function(prop, value){
55521         this.source[prop] = value;
55522         this.store.getById(prop).set('value', value);
55523     },
55524
55525     getSource : function(){
55526         return this.source;
55527     }
55528 });
55529
55530 Roo.grid.PropertyColumnModel = function(grid, store){
55531     this.grid = grid;
55532     var g = Roo.grid;
55533     g.PropertyColumnModel.superclass.constructor.call(this, [
55534         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55535         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55536     ]);
55537     this.store = store;
55538     this.bselect = Roo.DomHelper.append(document.body, {
55539         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55540             {tag: 'option', value: 'true', html: 'true'},
55541             {tag: 'option', value: 'false', html: 'false'}
55542         ]
55543     });
55544     Roo.id(this.bselect);
55545     var f = Roo.form;
55546     this.editors = {
55547         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55548         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55549         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55550         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55551         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55552     };
55553     this.renderCellDelegate = this.renderCell.createDelegate(this);
55554     this.renderPropDelegate = this.renderProp.createDelegate(this);
55555 };
55556
55557 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55558     
55559     
55560     nameText : 'Name',
55561     valueText : 'Value',
55562     
55563     dateFormat : 'm/j/Y',
55564     
55565     
55566     renderDate : function(dateVal){
55567         return dateVal.dateFormat(this.dateFormat);
55568     },
55569
55570     renderBool : function(bVal){
55571         return bVal ? 'true' : 'false';
55572     },
55573
55574     isCellEditable : function(colIndex, rowIndex){
55575         return colIndex == 1;
55576     },
55577
55578     getRenderer : function(col){
55579         return col == 1 ?
55580             this.renderCellDelegate : this.renderPropDelegate;
55581     },
55582
55583     renderProp : function(v){
55584         return this.getPropertyName(v);
55585     },
55586
55587     renderCell : function(val){
55588         var rv = val;
55589         if(val instanceof Date){
55590             rv = this.renderDate(val);
55591         }else if(typeof val == 'boolean'){
55592             rv = this.renderBool(val);
55593         }
55594         return Roo.util.Format.htmlEncode(rv);
55595     },
55596
55597     getPropertyName : function(name){
55598         var pn = this.grid.propertyNames;
55599         return pn && pn[name] ? pn[name] : name;
55600     },
55601
55602     getCellEditor : function(colIndex, rowIndex){
55603         var p = this.store.getProperty(rowIndex);
55604         var n = p.data['name'], val = p.data['value'];
55605         
55606         if(typeof(this.grid.customEditors[n]) == 'string'){
55607             return this.editors[this.grid.customEditors[n]];
55608         }
55609         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55610             return this.grid.customEditors[n];
55611         }
55612         if(val instanceof Date){
55613             return this.editors['date'];
55614         }else if(typeof val == 'number'){
55615             return this.editors['number'];
55616         }else if(typeof val == 'boolean'){
55617             return this.editors['boolean'];
55618         }else{
55619             return this.editors['string'];
55620         }
55621     }
55622 });
55623
55624 /**
55625  * @class Roo.grid.PropertyGrid
55626  * @extends Roo.grid.EditorGrid
55627  * This class represents the  interface of a component based property grid control.
55628  * <br><br>Usage:<pre><code>
55629  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55630       
55631  });
55632  // set any options
55633  grid.render();
55634  * </code></pre>
55635   
55636  * @constructor
55637  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55638  * The container MUST have some type of size defined for the grid to fill. The container will be
55639  * automatically set to position relative if it isn't already.
55640  * @param {Object} config A config object that sets properties on this grid.
55641  */
55642 Roo.grid.PropertyGrid = function(container, config){
55643     config = config || {};
55644     var store = new Roo.grid.PropertyStore(this);
55645     this.store = store;
55646     var cm = new Roo.grid.PropertyColumnModel(this, store);
55647     store.store.sort('name', 'ASC');
55648     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55649         ds: store.store,
55650         cm: cm,
55651         enableColLock:false,
55652         enableColumnMove:false,
55653         stripeRows:false,
55654         trackMouseOver: false,
55655         clicksToEdit:1
55656     }, config));
55657     this.getGridEl().addClass('x-props-grid');
55658     this.lastEditRow = null;
55659     this.on('columnresize', this.onColumnResize, this);
55660     this.addEvents({
55661          /**
55662              * @event beforepropertychange
55663              * Fires before a property changes (return false to stop?)
55664              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55665              * @param {String} id Record Id
55666              * @param {String} newval New Value
55667          * @param {String} oldval Old Value
55668              */
55669         "beforepropertychange": true,
55670         /**
55671              * @event propertychange
55672              * Fires after a property changes
55673              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55674              * @param {String} id Record Id
55675              * @param {String} newval New Value
55676          * @param {String} oldval Old Value
55677              */
55678         "propertychange": true
55679     });
55680     this.customEditors = this.customEditors || {};
55681 };
55682 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55683     
55684      /**
55685      * @cfg {Object} customEditors map of colnames=> custom editors.
55686      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55687      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55688      * false disables editing of the field.
55689          */
55690     
55691       /**
55692      * @cfg {Object} propertyNames map of property Names to their displayed value
55693          */
55694     
55695     render : function(){
55696         Roo.grid.PropertyGrid.superclass.render.call(this);
55697         this.autoSize.defer(100, this);
55698     },
55699
55700     autoSize : function(){
55701         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55702         if(this.view){
55703             this.view.fitColumns();
55704         }
55705     },
55706
55707     onColumnResize : function(){
55708         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55709         this.autoSize();
55710     },
55711     /**
55712      * Sets the data for the Grid
55713      * accepts a Key => Value object of all the elements avaiable.
55714      * @param {Object} data  to appear in grid.
55715      */
55716     setSource : function(source){
55717         this.store.setSource(source);
55718         //this.autoSize();
55719     },
55720     /**
55721      * Gets all the data from the grid.
55722      * @return {Object} data  data stored in grid
55723      */
55724     getSource : function(){
55725         return this.store.getSource();
55726     }
55727 });/*
55728  * Based on:
55729  * Ext JS Library 1.1.1
55730  * Copyright(c) 2006-2007, Ext JS, LLC.
55731  *
55732  * Originally Released Under LGPL - original licence link has changed is not relivant.
55733  *
55734  * Fork - LGPL
55735  * <script type="text/javascript">
55736  */
55737  
55738 /**
55739  * @class Roo.LoadMask
55740  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55741  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55742  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55743  * element's UpdateManager load indicator and will be destroyed after the initial load.
55744  * @constructor
55745  * Create a new LoadMask
55746  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55747  * @param {Object} config The config object
55748  */
55749 Roo.LoadMask = function(el, config){
55750     this.el = Roo.get(el);
55751     Roo.apply(this, config);
55752     if(this.store){
55753         this.store.on('beforeload', this.onBeforeLoad, this);
55754         this.store.on('load', this.onLoad, this);
55755         this.store.on('loadexception', this.onLoadException, this);
55756         this.removeMask = false;
55757     }else{
55758         var um = this.el.getUpdateManager();
55759         um.showLoadIndicator = false; // disable the default indicator
55760         um.on('beforeupdate', this.onBeforeLoad, this);
55761         um.on('update', this.onLoad, this);
55762         um.on('failure', this.onLoad, this);
55763         this.removeMask = true;
55764     }
55765 };
55766
55767 Roo.LoadMask.prototype = {
55768     /**
55769      * @cfg {Boolean} removeMask
55770      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55771      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55772      */
55773     /**
55774      * @cfg {String} msg
55775      * The text to display in a centered loading message box (defaults to 'Loading...')
55776      */
55777     msg : 'Loading...',
55778     /**
55779      * @cfg {String} msgCls
55780      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55781      */
55782     msgCls : 'x-mask-loading',
55783
55784     /**
55785      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55786      * @type Boolean
55787      */
55788     disabled: false,
55789
55790     /**
55791      * Disables the mask to prevent it from being displayed
55792      */
55793     disable : function(){
55794        this.disabled = true;
55795     },
55796
55797     /**
55798      * Enables the mask so that it can be displayed
55799      */
55800     enable : function(){
55801         this.disabled = false;
55802     },
55803     
55804     onLoadException : function()
55805     {
55806         Roo.log(arguments);
55807         
55808         if (typeof(arguments[3]) != 'undefined') {
55809             Roo.MessageBox.alert("Error loading",arguments[3]);
55810         } 
55811         /*
55812         try {
55813             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55814                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55815             }   
55816         } catch(e) {
55817             
55818         }
55819         */
55820     
55821         
55822         
55823         this.el.unmask(this.removeMask);
55824     },
55825     // private
55826     onLoad : function()
55827     {
55828         this.el.unmask(this.removeMask);
55829     },
55830
55831     // private
55832     onBeforeLoad : function(){
55833         if(!this.disabled){
55834             this.el.mask(this.msg, this.msgCls);
55835         }
55836     },
55837
55838     // private
55839     destroy : function(){
55840         if(this.store){
55841             this.store.un('beforeload', this.onBeforeLoad, this);
55842             this.store.un('load', this.onLoad, this);
55843             this.store.un('loadexception', this.onLoadException, this);
55844         }else{
55845             var um = this.el.getUpdateManager();
55846             um.un('beforeupdate', this.onBeforeLoad, this);
55847             um.un('update', this.onLoad, this);
55848             um.un('failure', this.onLoad, this);
55849         }
55850     }
55851 };/*
55852  * Based on:
55853  * Ext JS Library 1.1.1
55854  * Copyright(c) 2006-2007, Ext JS, LLC.
55855  *
55856  * Originally Released Under LGPL - original licence link has changed is not relivant.
55857  *
55858  * Fork - LGPL
55859  * <script type="text/javascript">
55860  */
55861
55862
55863 /**
55864  * @class Roo.XTemplate
55865  * @extends Roo.Template
55866  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55867 <pre><code>
55868 var t = new Roo.XTemplate(
55869         '&lt;select name="{name}"&gt;',
55870                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55871         '&lt;/select&gt;'
55872 );
55873  
55874 // then append, applying the master template values
55875  </code></pre>
55876  *
55877  * Supported features:
55878  *
55879  *  Tags:
55880
55881 <pre><code>
55882       {a_variable} - output encoded.
55883       {a_variable.format:("Y-m-d")} - call a method on the variable
55884       {a_variable:raw} - unencoded output
55885       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55886       {a_variable:this.method_on_template(...)} - call a method on the template object.
55887  
55888 </code></pre>
55889  *  The tpl tag:
55890 <pre><code>
55891         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55892         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55893         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55894         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55895   
55896         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55897         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55898 </code></pre>
55899  *      
55900  */
55901 Roo.XTemplate = function()
55902 {
55903     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55904     if (this.html) {
55905         this.compile();
55906     }
55907 };
55908
55909
55910 Roo.extend(Roo.XTemplate, Roo.Template, {
55911
55912     /**
55913      * The various sub templates
55914      */
55915     tpls : false,
55916     /**
55917      *
55918      * basic tag replacing syntax
55919      * WORD:WORD()
55920      *
55921      * // you can fake an object call by doing this
55922      *  x.t:(test,tesT) 
55923      * 
55924      */
55925     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55926
55927     /**
55928      * compile the template
55929      *
55930      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55931      *
55932      */
55933     compile: function()
55934     {
55935         var s = this.html;
55936      
55937         s = ['<tpl>', s, '</tpl>'].join('');
55938     
55939         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55940             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55941             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55942             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55943             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55944             m,
55945             id     = 0,
55946             tpls   = [];
55947     
55948         while(true == !!(m = s.match(re))){
55949             var forMatch   = m[0].match(nameRe),
55950                 ifMatch   = m[0].match(ifRe),
55951                 execMatch   = m[0].match(execRe),
55952                 namedMatch   = m[0].match(namedRe),
55953                 
55954                 exp  = null, 
55955                 fn   = null,
55956                 exec = null,
55957                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55958                 
55959             if (ifMatch) {
55960                 // if - puts fn into test..
55961                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55962                 if(exp){
55963                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55964                 }
55965             }
55966             
55967             if (execMatch) {
55968                 // exec - calls a function... returns empty if true is  returned.
55969                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55970                 if(exp){
55971                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55972                 }
55973             }
55974             
55975             
55976             if (name) {
55977                 // for = 
55978                 switch(name){
55979                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
55980                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
55981                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
55982                 }
55983             }
55984             var uid = namedMatch ? namedMatch[1] : id;
55985             
55986             
55987             tpls.push({
55988                 id:     namedMatch ? namedMatch[1] : id,
55989                 target: name,
55990                 exec:   exec,
55991                 test:   fn,
55992                 body:   m[1] || ''
55993             });
55994             if (namedMatch) {
55995                 s = s.replace(m[0], '');
55996             } else { 
55997                 s = s.replace(m[0], '{xtpl'+ id + '}');
55998             }
55999             ++id;
56000         }
56001         this.tpls = [];
56002         for(var i = tpls.length-1; i >= 0; --i){
56003             this.compileTpl(tpls[i]);
56004             this.tpls[tpls[i].id] = tpls[i];
56005         }
56006         this.master = tpls[tpls.length-1];
56007         return this;
56008     },
56009     /**
56010      * same as applyTemplate, except it's done to one of the subTemplates
56011      * when using named templates, you can do:
56012      *
56013      * var str = pl.applySubTemplate('your-name', values);
56014      *
56015      * 
56016      * @param {Number} id of the template
56017      * @param {Object} values to apply to template
56018      * @param {Object} parent (normaly the instance of this object)
56019      */
56020     applySubTemplate : function(id, values, parent)
56021     {
56022         
56023         
56024         var t = this.tpls[id];
56025         
56026         
56027         try { 
56028             if(t.test && !t.test.call(this, values, parent)){
56029                 return '';
56030             }
56031         } catch(e) {
56032             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
56033             Roo.log(e.toString());
56034             Roo.log(t.test);
56035             return ''
56036         }
56037         try { 
56038             
56039             if(t.exec && t.exec.call(this, values, parent)){
56040                 return '';
56041             }
56042         } catch(e) {
56043             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
56044             Roo.log(e.toString());
56045             Roo.log(t.exec);
56046             return ''
56047         }
56048         try {
56049             var vs = t.target ? t.target.call(this, values, parent) : values;
56050             parent = t.target ? values : parent;
56051             if(t.target && vs instanceof Array){
56052                 var buf = [];
56053                 for(var i = 0, len = vs.length; i < len; i++){
56054                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
56055                 }
56056                 return buf.join('');
56057             }
56058             return t.compiled.call(this, vs, parent);
56059         } catch (e) {
56060             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
56061             Roo.log(e.toString());
56062             Roo.log(t.compiled);
56063             return '';
56064         }
56065     },
56066
56067     compileTpl : function(tpl)
56068     {
56069         var fm = Roo.util.Format;
56070         var useF = this.disableFormats !== true;
56071         var sep = Roo.isGecko ? "+" : ",";
56072         var undef = function(str) {
56073             Roo.log("Property not found :"  + str);
56074             return '';
56075         };
56076         
56077         var fn = function(m, name, format, args)
56078         {
56079             //Roo.log(arguments);
56080             args = args ? args.replace(/\\'/g,"'") : args;
56081             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
56082             if (typeof(format) == 'undefined') {
56083                 format= 'htmlEncode';
56084             }
56085             if (format == 'raw' ) {
56086                 format = false;
56087             }
56088             
56089             if(name.substr(0, 4) == 'xtpl'){
56090                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
56091             }
56092             
56093             // build an array of options to determine if value is undefined..
56094             
56095             // basically get 'xxxx.yyyy' then do
56096             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
56097             //    (function () { Roo.log("Property not found"); return ''; })() :
56098             //    ......
56099             
56100             var udef_ar = [];
56101             var lookfor = '';
56102             Roo.each(name.split('.'), function(st) {
56103                 lookfor += (lookfor.length ? '.': '') + st;
56104                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
56105             });
56106             
56107             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
56108             
56109             
56110             if(format && useF){
56111                 
56112                 args = args ? ',' + args : "";
56113                  
56114                 if(format.substr(0, 5) != "this."){
56115                     format = "fm." + format + '(';
56116                 }else{
56117                     format = 'this.call("'+ format.substr(5) + '", ';
56118                     args = ", values";
56119                 }
56120                 
56121                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
56122             }
56123              
56124             if (args.length) {
56125                 // called with xxyx.yuu:(test,test)
56126                 // change to ()
56127                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
56128             }
56129             // raw.. - :raw modifier..
56130             return "'"+ sep + udef_st  + name + ")"+sep+"'";
56131             
56132         };
56133         var body;
56134         // branched to use + in gecko and [].join() in others
56135         if(Roo.isGecko){
56136             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
56137                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
56138                     "';};};";
56139         }else{
56140             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
56141             body.push(tpl.body.replace(/(\r\n|\n)/g,
56142                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
56143             body.push("'].join('');};};");
56144             body = body.join('');
56145         }
56146         
56147         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
56148        
56149         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
56150         eval(body);
56151         
56152         return this;
56153     },
56154
56155     applyTemplate : function(values){
56156         return this.master.compiled.call(this, values, {});
56157         //var s = this.subs;
56158     },
56159
56160     apply : function(){
56161         return this.applyTemplate.apply(this, arguments);
56162     }
56163
56164  });
56165
56166 Roo.XTemplate.from = function(el){
56167     el = Roo.getDom(el);
56168     return new Roo.XTemplate(el.value || el.innerHTML);
56169 };