Roo/bootstrap/Calendar.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @return {CompositeElement} this
10950     */
10951     filter : function(selector){
10952         var els = [];
10953         this.each(function(el){
10954             if(el.is(selector)){
10955                 els[els.length] = el.dom;
10956             }
10957         });
10958         this.fill(els);
10959         return this;
10960     },
10961
10962     invoke : function(fn, args){
10963         var els = this.elements;
10964         for(var i = 0, len = els.length; i < len; i++) {
10965                 Roo.Element.prototype[fn].apply(els[i], args);
10966         }
10967         return this;
10968     },
10969     /**
10970     * Adds elements to this composite.
10971     * @param {String/Array} els A string CSS selector, an array of elements or an element
10972     * @return {CompositeElement} this
10973     */
10974     add : function(els){
10975         if(typeof els == "string"){
10976             this.addElements(Roo.Element.selectorFunction(els));
10977         }else if(els.length !== undefined){
10978             this.addElements(els);
10979         }else{
10980             this.addElements([els]);
10981         }
10982         return this;
10983     },
10984     /**
10985     * Calls the passed function passing (el, this, index) for each element in this composite.
10986     * @param {Function} fn The function to call
10987     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10988     * @return {CompositeElement} this
10989     */
10990     each : function(fn, scope){
10991         var els = this.elements;
10992         for(var i = 0, len = els.length; i < len; i++){
10993             if(fn.call(scope || els[i], els[i], this, i) === false) {
10994                 break;
10995             }
10996         }
10997         return this;
10998     },
10999
11000     /**
11001      * Returns the Element object at the specified index
11002      * @param {Number} index
11003      * @return {Roo.Element}
11004      */
11005     item : function(index){
11006         return this.elements[index] || null;
11007     },
11008
11009     /**
11010      * Returns the first Element
11011      * @return {Roo.Element}
11012      */
11013     first : function(){
11014         return this.item(0);
11015     },
11016
11017     /**
11018      * Returns the last Element
11019      * @return {Roo.Element}
11020      */
11021     last : function(){
11022         return this.item(this.elements.length-1);
11023     },
11024
11025     /**
11026      * Returns the number of elements in this composite
11027      * @return Number
11028      */
11029     getCount : function(){
11030         return this.elements.length;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     contains : function(el){
11038         return this.indexOf(el) !== -1;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     indexOf : function(el){
11046         return this.elements.indexOf(Roo.get(el));
11047     },
11048
11049
11050     /**
11051     * Removes the specified element(s).
11052     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11053     * or an array of any of those.
11054     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11055     * @return {CompositeElement} this
11056     */
11057     removeElement : function(el, removeDom){
11058         if(el instanceof Array){
11059             for(var i = 0, len = el.length; i < len; i++){
11060                 this.removeElement(el[i]);
11061             }
11062             return this;
11063         }
11064         var index = typeof el == 'number' ? el : this.indexOf(el);
11065         if(index !== -1){
11066             if(removeDom){
11067                 var d = this.elements[index];
11068                 if(d.dom){
11069                     d.remove();
11070                 }else{
11071                     d.parentNode.removeChild(d);
11072                 }
11073             }
11074             this.elements.splice(index, 1);
11075         }
11076         return this;
11077     },
11078
11079     /**
11080     * Replaces the specified element with the passed element.
11081     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11082     * to replace.
11083     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11084     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11085     * @return {CompositeElement} this
11086     */
11087     replaceElement : function(el, replacement, domReplace){
11088         var index = typeof el == 'number' ? el : this.indexOf(el);
11089         if(index !== -1){
11090             if(domReplace){
11091                 this.elements[index].replaceWith(replacement);
11092             }else{
11093                 this.elements.splice(index, 1, Roo.get(replacement))
11094             }
11095         }
11096         return this;
11097     },
11098
11099     /**
11100      * Removes all elements.
11101      */
11102     clear : function(){
11103         this.elements = [];
11104     }
11105 };
11106 (function(){
11107     Roo.CompositeElement.createCall = function(proto, fnName){
11108         if(!proto[fnName]){
11109             proto[fnName] = function(){
11110                 return this.invoke(fnName, arguments);
11111             };
11112         }
11113     };
11114     for(var fnName in Roo.Element.prototype){
11115         if(typeof Roo.Element.prototype[fnName] == "function"){
11116             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11117         }
11118     };
11119 })();
11120 /*
11121  * Based on:
11122  * Ext JS Library 1.1.1
11123  * Copyright(c) 2006-2007, Ext JS, LLC.
11124  *
11125  * Originally Released Under LGPL - original licence link has changed is not relivant.
11126  *
11127  * Fork - LGPL
11128  * <script type="text/javascript">
11129  */
11130
11131 /**
11132  * @class Roo.CompositeElementLite
11133  * @extends Roo.CompositeElement
11134  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11135  <pre><code>
11136  var els = Roo.select("#some-el div.some-class");
11137  // or select directly from an existing element
11138  var el = Roo.get('some-el');
11139  el.select('div.some-class');
11140
11141  els.setWidth(100); // all elements become 100 width
11142  els.hide(true); // all elements fade out and hide
11143  // or
11144  els.setWidth(100).hide(true);
11145  </code></pre><br><br>
11146  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11147  * actions will be performed on all the elements in this collection.</b>
11148  */
11149 Roo.CompositeElementLite = function(els){
11150     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11151     this.el = new Roo.Element.Flyweight();
11152 };
11153 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11154     addElements : function(els){
11155         if(els){
11156             if(els instanceof Array){
11157                 this.elements = this.elements.concat(els);
11158             }else{
11159                 var yels = this.elements;
11160                 var index = yels.length-1;
11161                 for(var i = 0, len = els.length; i < len; i++) {
11162                     yels[++index] = els[i];
11163                 }
11164             }
11165         }
11166         return this;
11167     },
11168     invoke : function(fn, args){
11169         var els = this.elements;
11170         var el = this.el;
11171         for(var i = 0, len = els.length; i < len; i++) {
11172             el.dom = els[i];
11173                 Roo.Element.prototype[fn].apply(el, args);
11174         }
11175         return this;
11176     },
11177     /**
11178      * Returns a flyweight Element of the dom element object at the specified index
11179      * @param {Number} index
11180      * @return {Roo.Element}
11181      */
11182     item : function(index){
11183         if(!this.elements[index]){
11184             return null;
11185         }
11186         this.el.dom = this.elements[index];
11187         return this.el;
11188     },
11189
11190     // fixes scope with flyweight
11191     addListener : function(eventName, handler, scope, opt){
11192         var els = this.elements;
11193         for(var i = 0, len = els.length; i < len; i++) {
11194             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11195         }
11196         return this;
11197     },
11198
11199     /**
11200     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11201     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11202     * a reference to the dom node, use el.dom.</b>
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         var el = this.el;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             el.dom = els[i];
11212                 if(fn.call(scope || el, el, this, i) === false){
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.getDom(el));
11221     },
11222
11223     replaceElement : function(el, replacement, domReplace){
11224         var index = typeof el == 'number' ? el : this.indexOf(el);
11225         if(index !== -1){
11226             replacement = Roo.getDom(replacement);
11227             if(domReplace){
11228                 var d = this.elements[index];
11229                 d.parentNode.insertBefore(replacement, d);
11230                 d.parentNode.removeChild(d);
11231             }
11232             this.elements.splice(index, 1, replacement);
11233         }
11234         return this;
11235     }
11236 });
11237 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11238
11239 /*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250  
11251
11252 /**
11253  * @class Roo.data.Connection
11254  * @extends Roo.util.Observable
11255  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11256  * either to a configured URL, or to a URL specified at request time.<br><br>
11257  * <p>
11258  * Requests made by this class are asynchronous, and will return immediately. No data from
11259  * the server will be available to the statement immediately following the {@link #request} call.
11260  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11261  * <p>
11262  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11263  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11264  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11265  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11266  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11267  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11268  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11269  * standard DOM methods.
11270  * @constructor
11271  * @param {Object} config a configuration object.
11272  */
11273 Roo.data.Connection = function(config){
11274     Roo.apply(this, config);
11275     this.addEvents({
11276         /**
11277          * @event beforerequest
11278          * Fires before a network request is made to retrieve a data object.
11279          * @param {Connection} conn This Connection object.
11280          * @param {Object} options The options config object passed to the {@link #request} method.
11281          */
11282         "beforerequest" : true,
11283         /**
11284          * @event requestcomplete
11285          * Fires if the request was successfully completed.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestcomplete" : true,
11292         /**
11293          * @event requestexception
11294          * Fires if an error HTTP status was returned from the server.
11295          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11296          * @param {Connection} conn This Connection object.
11297          * @param {Object} response The XHR object containing the response data.
11298          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11299          * @param {Object} options The options config object passed to the {@link #request} method.
11300          */
11301         "requestexception" : true
11302     });
11303     Roo.data.Connection.superclass.constructor.call(this);
11304 };
11305
11306 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11307     /**
11308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11312      * extra parameters to each request made by this object. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11316      *  to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11320      */
11321     /**
11322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11323      */
11324     timeout : 30000,
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11327      * @type Boolean
11328      */
11329     autoAbort:false,
11330
11331     /**
11332      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11333      * @type Boolean
11334      */
11335     disableCaching: true,
11336
11337     /**
11338      * Sends an HTTP request to a remote server.
11339      * @param {Object} options An object which may contain the following properties:<ul>
11340      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11341      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11342      * request, a url encoded string or a function to call to get either.</li>
11343      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11344      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11345      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11346      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11347      * <li>options {Object} The parameter to the request call.</li>
11348      * <li>success {Boolean} True if the request succeeded.</li>
11349      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11350      * </ul></li>
11351      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11352      * The callback is passed the following parameters:<ul>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * <li>options {Object} The parameter to the request call.</li>
11355      * </ul></li>
11356      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11357      * The callback is passed the following parameters:<ul>
11358      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11359      * <li>options {Object} The parameter to the request call.</li>
11360      * </ul></li>
11361      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11362      * for the callback function. Defaults to the browser window.</li>
11363      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11364      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11365      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11366      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11367      * params for the post data. Any params will be appended to the URL.</li>
11368      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11369      * </ul>
11370      * @return {Number} transactionId
11371      */
11372     request : function(o){
11373         if(this.fireEvent("beforerequest", this, o) !== false){
11374             var p = o.params;
11375
11376             if(typeof p == "function"){
11377                 p = p.call(o.scope||window, o);
11378             }
11379             if(typeof p == "object"){
11380                 p = Roo.urlEncode(o.params);
11381             }
11382             if(this.extraParams){
11383                 var extras = Roo.urlEncode(this.extraParams);
11384                 p = p ? (p + '&' + extras) : extras;
11385             }
11386
11387             var url = o.url || this.url;
11388             if(typeof url == 'function'){
11389                 url = url.call(o.scope||window, o);
11390             }
11391
11392             if(o.form){
11393                 var form = Roo.getDom(o.form);
11394                 url = url || form.action;
11395
11396                 var enctype = form.getAttribute("enctype");
11397                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11398                     return this.doFormUpload(o, p, url);
11399                 }
11400                 var f = Roo.lib.Ajax.serializeForm(form);
11401                 p = p ? (p + '&' + f) : f;
11402             }
11403
11404             var hs = o.headers;
11405             if(this.defaultHeaders){
11406                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11407                 if(!o.headers){
11408                     o.headers = hs;
11409                 }
11410             }
11411
11412             var cb = {
11413                 success: this.handleResponse,
11414                 failure: this.handleFailure,
11415                 scope: this,
11416                 argument: {options: o},
11417                 timeout : o.timeout || this.timeout
11418             };
11419
11420             var method = o.method||this.method||(p ? "POST" : "GET");
11421
11422             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11423                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11424             }
11425
11426             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11427                 if(o.autoAbort){
11428                     this.abort();
11429                 }
11430             }else if(this.autoAbort !== false){
11431                 this.abort();
11432             }
11433
11434             if((method == 'GET' && p) || o.xmlData){
11435                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11436                 p = '';
11437             }
11438             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11439             return this.transId;
11440         }else{
11441             Roo.callback(o.callback, o.scope, [o, null, null]);
11442             return null;
11443         }
11444     },
11445
11446     /**
11447      * Determine whether this object has a request outstanding.
11448      * @param {Number} transactionId (Optional) defaults to the last transaction
11449      * @return {Boolean} True if there is an outstanding request.
11450      */
11451     isLoading : function(transId){
11452         if(transId){
11453             return Roo.lib.Ajax.isCallInProgress(transId);
11454         }else{
11455             return this.transId ? true : false;
11456         }
11457     },
11458
11459     /**
11460      * Aborts any outstanding request.
11461      * @param {Number} transactionId (Optional) defaults to the last transaction
11462      */
11463     abort : function(transId){
11464         if(transId || this.isLoading()){
11465             Roo.lib.Ajax.abort(transId || this.transId);
11466         }
11467     },
11468
11469     // private
11470     handleResponse : function(response){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestcomplete", this, response, options);
11475         Roo.callback(options.success, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, true, response]);
11477     },
11478
11479     // private
11480     handleFailure : function(response, e){
11481         this.transId = false;
11482         var options = response.argument.options;
11483         response.argument = options ? options.argument : null;
11484         this.fireEvent("requestexception", this, response, options, e);
11485         Roo.callback(options.failure, options.scope, [response, options]);
11486         Roo.callback(options.callback, options.scope, [options, false, response]);
11487     },
11488
11489     // private
11490     doFormUpload : function(o, ps, url){
11491         var id = Roo.id();
11492         var frame = document.createElement('iframe');
11493         frame.id = id;
11494         frame.name = id;
11495         frame.className = 'x-hidden';
11496         if(Roo.isIE){
11497             frame.src = Roo.SSL_SECURE_URL;
11498         }
11499         document.body.appendChild(frame);
11500
11501         if(Roo.isIE){
11502            document.frames[id].name = id;
11503         }
11504
11505         var form = Roo.getDom(o.form);
11506         form.target = id;
11507         form.method = 'POST';
11508         form.enctype = form.encoding = 'multipart/form-data';
11509         if(url){
11510             form.action = url;
11511         }
11512
11513         var hiddens, hd;
11514         if(ps){ // add dynamic params
11515             hiddens = [];
11516             ps = Roo.urlDecode(ps, false);
11517             for(var k in ps){
11518                 if(ps.hasOwnProperty(k)){
11519                     hd = document.createElement('input');
11520                     hd.type = 'hidden';
11521                     hd.name = k;
11522                     hd.value = ps[k];
11523                     form.appendChild(hd);
11524                     hiddens.push(hd);
11525                 }
11526             }
11527         }
11528
11529         function cb(){
11530             var r = {  // bogus response object
11531                 responseText : '',
11532                 responseXML : null
11533             };
11534
11535             r.argument = o ? o.argument : null;
11536
11537             try { //
11538                 var doc;
11539                 if(Roo.isIE){
11540                     doc = frame.contentWindow.document;
11541                 }else {
11542                     doc = (frame.contentDocument || window.frames[id].document);
11543                 }
11544                 if(doc && doc.body){
11545                     r.responseText = doc.body.innerHTML;
11546                 }
11547                 if(doc && doc.XMLDocument){
11548                     r.responseXML = doc.XMLDocument;
11549                 }else {
11550                     r.responseXML = doc;
11551                 }
11552             }
11553             catch(e) {
11554                 // ignore
11555             }
11556
11557             Roo.EventManager.removeListener(frame, 'load', cb, this);
11558
11559             this.fireEvent("requestcomplete", this, r, o);
11560             Roo.callback(o.success, o.scope, [r, o]);
11561             Roo.callback(o.callback, o.scope, [o, true, r]);
11562
11563             setTimeout(function(){document.body.removeChild(frame);}, 100);
11564         }
11565
11566         Roo.EventManager.on(frame, 'load', cb, this);
11567         form.submit();
11568
11569         if(hiddens){ // remove dynamic params
11570             for(var i = 0, len = hiddens.length; i < len; i++){
11571                 form.removeChild(hiddens[i]);
11572             }
11573         }
11574     }
11575 });
11576 /*
11577  * Based on:
11578  * Ext JS Library 1.1.1
11579  * Copyright(c) 2006-2007, Ext JS, LLC.
11580  *
11581  * Originally Released Under LGPL - original licence link has changed is not relivant.
11582  *
11583  * Fork - LGPL
11584  * <script type="text/javascript">
11585  */
11586  
11587 /**
11588  * Global Ajax request class.
11589  * 
11590  * @class Roo.Ajax
11591  * @extends Roo.data.Connection
11592  * @static
11593  * 
11594  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11595  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11596  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11597  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11598  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11599  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11600  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11601  */
11602 Roo.Ajax = new Roo.data.Connection({
11603     // fix up the docs
11604     /**
11605      * @scope Roo.Ajax
11606      * @type {Boolear} 
11607      */
11608     autoAbort : false,
11609
11610     /**
11611      * Serialize the passed form into a url encoded string
11612      * @scope Roo.Ajax
11613      * @param {String/HTMLElement} form
11614      * @return {String}
11615      */
11616     serializeForm : function(form){
11617         return Roo.lib.Ajax.serializeForm(form);
11618     }
11619 });/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630  
11631 /**
11632  * @class Roo.UpdateManager
11633  * @extends Roo.util.Observable
11634  * Provides AJAX-style update for Element object.<br><br>
11635  * Usage:<br>
11636  * <pre><code>
11637  * // Get it from a Roo.Element object
11638  * var el = Roo.get("foo");
11639  * var mgr = el.getUpdateManager();
11640  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11641  * ...
11642  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11643  * <br>
11644  * // or directly (returns the same UpdateManager instance)
11645  * var mgr = new Roo.UpdateManager("myElementId");
11646  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11647  * mgr.on("update", myFcnNeedsToKnow);
11648  * <br>
11649    // short handed call directly from the element object
11650    Roo.get("foo").load({
11651         url: "bar.php",
11652         scripts:true,
11653         params: "for=bar",
11654         text: "Loading Foo..."
11655    });
11656  * </code></pre>
11657  * @constructor
11658  * Create new UpdateManager directly.
11659  * @param {String/HTMLElement/Roo.Element} el The element to update
11660  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11661  */
11662 Roo.UpdateManager = function(el, forceNew){
11663     el = Roo.get(el);
11664     if(!forceNew && el.updateManager){
11665         return el.updateManager;
11666     }
11667     /**
11668      * The Element object
11669      * @type Roo.Element
11670      */
11671     this.el = el;
11672     /**
11673      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11674      * @type String
11675      */
11676     this.defaultUrl = null;
11677
11678     this.addEvents({
11679         /**
11680          * @event beforeupdate
11681          * Fired before an update is made, return false from your handler and the update is cancelled.
11682          * @param {Roo.Element} el
11683          * @param {String/Object/Function} url
11684          * @param {String/Object} params
11685          */
11686         "beforeupdate": true,
11687         /**
11688          * @event update
11689          * Fired after successful update is made.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "update": true,
11694         /**
11695          * @event failure
11696          * Fired on update failure.
11697          * @param {Roo.Element} el
11698          * @param {Object} oResponseObject The response Object
11699          */
11700         "failure": true
11701     });
11702     var d = Roo.UpdateManager.defaults;
11703     /**
11704      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11705      * @type String
11706      */
11707     this.sslBlankUrl = d.sslBlankUrl;
11708     /**
11709      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11710      * @type Boolean
11711      */
11712     this.disableCaching = d.disableCaching;
11713     /**
11714      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11715      * @type String
11716      */
11717     this.indicatorText = d.indicatorText;
11718     /**
11719      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11720      * @type String
11721      */
11722     this.showLoadIndicator = d.showLoadIndicator;
11723     /**
11724      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11725      * @type Number
11726      */
11727     this.timeout = d.timeout;
11728
11729     /**
11730      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11731      * @type Boolean
11732      */
11733     this.loadScripts = d.loadScripts;
11734
11735     /**
11736      * Transaction object of current executing transaction
11737      */
11738     this.transaction = null;
11739
11740     /**
11741      * @private
11742      */
11743     this.autoRefreshProcId = null;
11744     /**
11745      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.refreshDelegate = this.refresh.createDelegate(this);
11749     /**
11750      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11751      * @type Function
11752      */
11753     this.updateDelegate = this.update.createDelegate(this);
11754     /**
11755      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11756      * @type Function
11757      */
11758     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11759     /**
11760      * @private
11761      */
11762     this.successDelegate = this.processSuccess.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.failureDelegate = this.processFailure.createDelegate(this);
11767
11768     if(!this.renderer){
11769      /**
11770       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11771       */
11772     this.renderer = new Roo.UpdateManager.BasicRenderer();
11773     }
11774     
11775     Roo.UpdateManager.superclass.constructor.call(this);
11776 };
11777
11778 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11779     /**
11780      * Get the Element this UpdateManager is bound to
11781      * @return {Roo.Element} The element
11782      */
11783     getEl : function(){
11784         return this.el;
11785     },
11786     /**
11787      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11788      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11789 <pre><code>
11790 um.update({<br/>
11791     url: "your-url.php",<br/>
11792     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11793     callback: yourFunction,<br/>
11794     scope: yourObject, //(optional scope)  <br/>
11795     discardUrl: false, <br/>
11796     nocache: false,<br/>
11797     text: "Loading...",<br/>
11798     timeout: 30,<br/>
11799     scripts: false<br/>
11800 });
11801 </code></pre>
11802      * The only required property is url. The optional properties nocache, text and scripts
11803      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11804      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11805      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11806      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11807      */
11808     update : function(url, params, callback, discardUrl){
11809         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11810             var method = this.method,
11811                 cfg;
11812             if(typeof url == "object"){ // must be config object
11813                 cfg = url;
11814                 url = cfg.url;
11815                 params = params || cfg.params;
11816                 callback = callback || cfg.callback;
11817                 discardUrl = discardUrl || cfg.discardUrl;
11818                 if(callback && cfg.scope){
11819                     callback = callback.createDelegate(cfg.scope);
11820                 }
11821                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11822                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11823                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11824                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11825                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11826             }
11827             this.showLoading();
11828             if(!discardUrl){
11829                 this.defaultUrl = url;
11830             }
11831             if(typeof url == "function"){
11832                 url = url.call(this);
11833             }
11834
11835             method = method || (params ? "POST" : "GET");
11836             if(method == "GET"){
11837                 url = this.prepareUrl(url);
11838             }
11839
11840             var o = Roo.apply(cfg ||{}, {
11841                 url : url,
11842                 params: params,
11843                 success: this.successDelegate,
11844                 failure: this.failureDelegate,
11845                 callback: undefined,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11848             });
11849             Roo.log("updated manager called with timeout of " + o.timeout);
11850             this.transaction = Roo.Ajax.request(o);
11851         }
11852     },
11853
11854     /**
11855      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11856      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11857      * @param {String/HTMLElement} form The form Id or form element
11858      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11859      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11861      */
11862     formUpdate : function(form, url, reset, callback){
11863         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11864             if(typeof url == "function"){
11865                 url = url.call(this);
11866             }
11867             form = Roo.getDom(form);
11868             this.transaction = Roo.Ajax.request({
11869                 form: form,
11870                 url:url,
11871                 success: this.successDelegate,
11872                 failure: this.failureDelegate,
11873                 timeout: (this.timeout*1000),
11874                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11875             });
11876             this.showLoading.defer(1, this);
11877         }
11878     },
11879
11880     /**
11881      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11883      */
11884     refresh : function(callback){
11885         if(this.defaultUrl == null){
11886             return;
11887         }
11888         this.update(this.defaultUrl, null, callback, true);
11889     },
11890
11891     /**
11892      * Set this element to auto refresh.
11893      * @param {Number} interval How often to update (in seconds).
11894      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11895      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11898      */
11899     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11900         if(refreshNow){
11901             this.update(url || this.defaultUrl, params, callback, true);
11902         }
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905         }
11906         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11907     },
11908
11909     /**
11910      * Stop auto refresh on this element.
11911      */
11912      stopAutoRefresh : function(){
11913         if(this.autoRefreshProcId){
11914             clearInterval(this.autoRefreshProcId);
11915             delete this.autoRefreshProcId;
11916         }
11917     },
11918
11919     isAutoRefreshing : function(){
11920        return this.autoRefreshProcId ? true : false;
11921     },
11922     /**
11923      * Called to update the element to "Loading" state. Override to perform custom action.
11924      */
11925     showLoading : function(){
11926         if(this.showLoadIndicator){
11927             this.el.update(this.indicatorText);
11928         }
11929     },
11930
11931     /**
11932      * Adds unique parameter to query string if disableCaching = true
11933      * @private
11934      */
11935     prepareUrl : function(url){
11936         if(this.disableCaching){
11937             var append = "_dc=" + (new Date().getTime());
11938             if(url.indexOf("?") !== -1){
11939                 url += "&" + append;
11940             }else{
11941                 url += "?" + append;
11942             }
11943         }
11944         return url;
11945     },
11946
11947     /**
11948      * @private
11949      */
11950     processSuccess : function(response){
11951         this.transaction = null;
11952         if(response.argument.form && response.argument.reset){
11953             try{ // put in try/catch since some older FF releases had problems with this
11954                 response.argument.form.reset();
11955             }catch(e){}
11956         }
11957         if(this.loadScripts){
11958             this.renderer.render(this.el, response, this,
11959                 this.updateComplete.createDelegate(this, [response]));
11960         }else{
11961             this.renderer.render(this.el, response, this);
11962             this.updateComplete(response);
11963         }
11964     },
11965
11966     updateComplete : function(response){
11967         this.fireEvent("update", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, true, response);
11970         }
11971     },
11972
11973     /**
11974      * @private
11975      */
11976     processFailure : function(response){
11977         this.transaction = null;
11978         this.fireEvent("failure", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, false, response);
11981         }
11982     },
11983
11984     /**
11985      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11986      * @param {Object} renderer The object implementing the render() method
11987      */
11988     setRenderer : function(renderer){
11989         this.renderer = renderer;
11990     },
11991
11992     getRenderer : function(){
11993        return this.renderer;
11994     },
11995
11996     /**
11997      * Set the defaultUrl used for updates
11998      * @param {String/Function} defaultUrl The url or a function to call to get the url
11999      */
12000     setDefaultUrl : function(defaultUrl){
12001         this.defaultUrl = defaultUrl;
12002     },
12003
12004     /**
12005      * Aborts the executing transaction
12006      */
12007     abort : function(){
12008         if(this.transaction){
12009             Roo.Ajax.abort(this.transaction);
12010         }
12011     },
12012
12013     /**
12014      * Returns true if an update is in progress
12015      * @return {Boolean}
12016      */
12017     isUpdating : function(){
12018         if(this.transaction){
12019             return Roo.Ajax.isLoading(this.transaction);
12020         }
12021         return false;
12022     }
12023 });
12024
12025 /**
12026  * @class Roo.UpdateManager.defaults
12027  * @static (not really - but it helps the doc tool)
12028  * The defaults collection enables customizing the default properties of UpdateManager
12029  */
12030    Roo.UpdateManager.defaults = {
12031        /**
12032          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12033          * @type Number
12034          */
12035          timeout : 30,
12036
12037          /**
12038          * True to process scripts by default (Defaults to false).
12039          * @type Boolean
12040          */
12041         loadScripts : false,
12042
12043         /**
12044         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12045         * @type String
12046         */
12047         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12048         /**
12049          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12050          * @type Boolean
12051          */
12052         disableCaching : false,
12053         /**
12054          * Whether to show indicatorText when loading (Defaults to true).
12055          * @type Boolean
12056          */
12057         showLoadIndicator : true,
12058         /**
12059          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12060          * @type String
12061          */
12062         indicatorText : '<div class="loading-indicator">Loading...</div>'
12063    };
12064
12065 /**
12066  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12067  *Usage:
12068  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12069  * @param {String/HTMLElement/Roo.Element} el The element to update
12070  * @param {String} url The url
12071  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12072  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12073  * @static
12074  * @deprecated
12075  * @member Roo.UpdateManager
12076  */
12077 Roo.UpdateManager.updateElement = function(el, url, params, options){
12078     var um = Roo.get(el, true).getUpdateManager();
12079     Roo.apply(um, options);
12080     um.update(url, params, options ? options.callback : null);
12081 };
12082 // alias for backwards compat
12083 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12084 /**
12085  * @class Roo.UpdateManager.BasicRenderer
12086  * Default Content renderer. Updates the elements innerHTML with the responseText.
12087  */
12088 Roo.UpdateManager.BasicRenderer = function(){};
12089
12090 Roo.UpdateManager.BasicRenderer.prototype = {
12091     /**
12092      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12093      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12094      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12095      * @param {Roo.Element} el The element being rendered
12096      * @param {Object} response The YUI Connect response object
12097      * @param {UpdateManager} updateManager The calling update manager
12098      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12099      */
12100      render : function(el, response, updateManager, callback){
12101         el.update(response.responseText, updateManager.loadScripts, callback);
12102     }
12103 };
12104 /*
12105  * Based on:
12106  * Roo JS
12107  * (c)) Alan Knowles
12108  * Licence : LGPL
12109  */
12110
12111
12112 /**
12113  * @class Roo.DomTemplate
12114  * @extends Roo.Template
12115  * An effort at a dom based template engine..
12116  *
12117  * Similar to XTemplate, except it uses dom parsing to create the template..
12118  *
12119  * Supported features:
12120  *
12121  *  Tags:
12122
12123 <pre><code>
12124       {a_variable} - output encoded.
12125       {a_variable.format:("Y-m-d")} - call a method on the variable
12126       {a_variable:raw} - unencoded output
12127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12128       {a_variable:this.method_on_template(...)} - call a method on the template object.
12129  
12130 </code></pre>
12131  *  The tpl tag:
12132 <pre><code>
12133         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12134         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12135         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12136         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12137   
12138 </code></pre>
12139  *      
12140  */
12141 Roo.DomTemplate = function()
12142 {
12143      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12144      if (this.html) {
12145         this.compile();
12146      }
12147 };
12148
12149
12150 Roo.extend(Roo.DomTemplate, Roo.Template, {
12151     /**
12152      * id counter for sub templates.
12153      */
12154     id : 0,
12155     /**
12156      * flag to indicate if dom parser is inside a pre,
12157      * it will strip whitespace if not.
12158      */
12159     inPre : false,
12160     
12161     /**
12162      * The various sub templates
12163      */
12164     tpls : false,
12165     
12166     
12167     
12168     /**
12169      *
12170      * basic tag replacing syntax
12171      * WORD:WORD()
12172      *
12173      * // you can fake an object call by doing this
12174      *  x.t:(test,tesT) 
12175      * 
12176      */
12177     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12178     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12179     
12180     iterChild : function (node, method) {
12181         
12182         var oldPre = this.inPre;
12183         if (node.tagName == 'PRE') {
12184             this.inPre = true;
12185         }
12186         for( var i = 0; i < node.childNodes.length; i++) {
12187             method.call(this, node.childNodes[i]);
12188         }
12189         this.inPre = oldPre;
12190     },
12191     
12192     
12193     
12194     /**
12195      * compile the template
12196      *
12197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12198      *
12199      */
12200     compile: function()
12201     {
12202         var s = this.html;
12203         
12204         // covert the html into DOM...
12205         var doc = false;
12206         var div =false;
12207         try {
12208             doc = document.implementation.createHTMLDocument("");
12209             doc.documentElement.innerHTML =   this.html  ;
12210             div = doc.documentElement;
12211         } catch (e) {
12212             // old IE... - nasty -- it causes all sorts of issues.. with
12213             // images getting pulled from server..
12214             div = document.createElement('div');
12215             div.innerHTML = this.html;
12216         }
12217         //doc.documentElement.innerHTML = htmlBody
12218          
12219         
12220         
12221         this.tpls = [];
12222         var _t = this;
12223         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12224         
12225         var tpls = this.tpls;
12226         
12227         // create a top level template from the snippet..
12228         
12229         //Roo.log(div.innerHTML);
12230         
12231         var tpl = {
12232             uid : 'master',
12233             id : this.id++,
12234             attr : false,
12235             value : false,
12236             body : div.innerHTML,
12237             
12238             forCall : false,
12239             execCall : false,
12240             dom : div,
12241             isTop : true
12242             
12243         };
12244         tpls.unshift(tpl);
12245         
12246         
12247         // compile them...
12248         this.tpls = [];
12249         Roo.each(tpls, function(tp){
12250             this.compileTpl(tp);
12251             this.tpls[tp.id] = tp;
12252         }, this);
12253         
12254         this.master = tpls[0];
12255         return this;
12256         
12257         
12258     },
12259     
12260     compileNode : function(node, istop) {
12261         // test for
12262         //Roo.log(node);
12263         
12264         
12265         // skip anything not a tag..
12266         if (node.nodeType != 1) {
12267             if (node.nodeType == 3 && !this.inPre) {
12268                 // reduce white space..
12269                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12270                 
12271             }
12272             return;
12273         }
12274         
12275         var tpl = {
12276             uid : false,
12277             id : false,
12278             attr : false,
12279             value : false,
12280             body : '',
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : false,
12285             isTop : istop
12286             
12287             
12288         };
12289         
12290         
12291         switch(true) {
12292             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12293             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12294             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12295             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12296             // no default..
12297         }
12298         
12299         
12300         if (!tpl.attr) {
12301             // just itterate children..
12302             this.iterChild(node,this.compileNode);
12303             return;
12304         }
12305         tpl.uid = this.id++;
12306         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12307         node.removeAttribute('roo-'+ tpl.attr);
12308         if (tpl.attr != 'name') {
12309             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12310             node.parentNode.replaceChild(placeholder,  node);
12311         } else {
12312             
12313             var placeholder =  document.createElement('span');
12314             placeholder.className = 'roo-tpl-' + tpl.value;
12315             node.parentNode.replaceChild(placeholder,  node);
12316         }
12317         
12318         // parent now sees '{domtplXXXX}
12319         this.iterChild(node,this.compileNode);
12320         
12321         // we should now have node body...
12322         var div = document.createElement('div');
12323         div.appendChild(node);
12324         tpl.dom = node;
12325         // this has the unfortunate side effect of converting tagged attributes
12326         // eg. href="{...}" into %7C...%7D
12327         // this has been fixed by searching for those combo's although it's a bit hacky..
12328         
12329         
12330         tpl.body = div.innerHTML;
12331         
12332         
12333          
12334         tpl.id = tpl.uid;
12335         switch(tpl.attr) {
12336             case 'for' :
12337                 switch (tpl.value) {
12338                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12339                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12340                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12341                 }
12342                 break;
12343             
12344             case 'exec':
12345                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12346                 break;
12347             
12348             case 'if':     
12349                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'name':
12353                 tpl.id  = tpl.value; // replace non characters???
12354                 break;
12355             
12356         }
12357         
12358         
12359         this.tpls.push(tpl);
12360         
12361         
12362         
12363     },
12364     
12365     
12366     
12367     
12368     /**
12369      * Compile a segment of the template into a 'sub-template'
12370      *
12371      * 
12372      * 
12373      *
12374      */
12375     compileTpl : function(tpl)
12376     {
12377         var fm = Roo.util.Format;
12378         var useF = this.disableFormats !== true;
12379         
12380         var sep = Roo.isGecko ? "+\n" : ",\n";
12381         
12382         var undef = function(str) {
12383             Roo.debug && Roo.log("Property not found :"  + str);
12384             return '';
12385         };
12386           
12387         //Roo.log(tpl.body);
12388         
12389         
12390         
12391         var fn = function(m, lbrace, name, format, args)
12392         {
12393             //Roo.log("ARGS");
12394             //Roo.log(arguments);
12395             args = args ? args.replace(/\\'/g,"'") : args;
12396             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12397             if (typeof(format) == 'undefined') {
12398                 format =  'htmlEncode'; 
12399             }
12400             if (format == 'raw' ) {
12401                 format = false;
12402             }
12403             
12404             if(name.substr(0, 6) == 'domtpl'){
12405                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12406             }
12407             
12408             // build an array of options to determine if value is undefined..
12409             
12410             // basically get 'xxxx.yyyy' then do
12411             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12412             //    (function () { Roo.log("Property not found"); return ''; })() :
12413             //    ......
12414             
12415             var udef_ar = [];
12416             var lookfor = '';
12417             Roo.each(name.split('.'), function(st) {
12418                 lookfor += (lookfor.length ? '.': '') + st;
12419                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12420             });
12421             
12422             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12423             
12424             
12425             if(format && useF){
12426                 
12427                 args = args ? ',' + args : "";
12428                  
12429                 if(format.substr(0, 5) != "this."){
12430                     format = "fm." + format + '(';
12431                 }else{
12432                     format = 'this.call("'+ format.substr(5) + '", ';
12433                     args = ", values";
12434                 }
12435                 
12436                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12437             }
12438              
12439             if (args && args.length) {
12440                 // called with xxyx.yuu:(test,test)
12441                 // change to ()
12442                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12443             }
12444             // raw.. - :raw modifier..
12445             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12446             
12447         };
12448         var body;
12449         // branched to use + in gecko and [].join() in others
12450         if(Roo.isGecko){
12451             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12452                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12453                     "';};};";
12454         }else{
12455             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12456             body.push(tpl.body.replace(/(\r\n|\n)/g,
12457                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12458             body.push("'].join('');};};");
12459             body = body.join('');
12460         }
12461         
12462         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12463        
12464         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12465         eval(body);
12466         
12467         return this;
12468     },
12469      
12470     /**
12471      * same as applyTemplate, except it's done to one of the subTemplates
12472      * when using named templates, you can do:
12473      *
12474      * var str = pl.applySubTemplate('your-name', values);
12475      *
12476      * 
12477      * @param {Number} id of the template
12478      * @param {Object} values to apply to template
12479      * @param {Object} parent (normaly the instance of this object)
12480      */
12481     applySubTemplate : function(id, values, parent)
12482     {
12483         
12484         
12485         var t = this.tpls[id];
12486         
12487         
12488         try { 
12489             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12490                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12491                 return '';
12492             }
12493         } catch(e) {
12494             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12495             Roo.log(values);
12496           
12497             return '';
12498         }
12499         try { 
12500             
12501             if(t.execCall && t.execCall.call(this, values, parent)){
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507             return '';
12508         }
12509         
12510         try {
12511             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12512             parent = t.target ? values : parent;
12513             if(t.forCall && vs instanceof Array){
12514                 var buf = [];
12515                 for(var i = 0, len = vs.length; i < len; i++){
12516                     try {
12517                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12518                     } catch (e) {
12519                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12520                         Roo.log(e.body);
12521                         //Roo.log(t.compiled);
12522                         Roo.log(vs[i]);
12523                     }   
12524                 }
12525                 return buf.join('');
12526             }
12527         } catch (e) {
12528             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12529             Roo.log(values);
12530             return '';
12531         }
12532         try {
12533             return t.compiled.call(this, vs, parent);
12534         } catch (e) {
12535             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12536             Roo.log(e.body);
12537             //Roo.log(t.compiled);
12538             Roo.log(values);
12539             return '';
12540         }
12541     },
12542
12543    
12544
12545     applyTemplate : function(values){
12546         return this.master.compiled.call(this, values, {});
12547         //var s = this.subs;
12548     },
12549
12550     apply : function(){
12551         return this.applyTemplate.apply(this, arguments);
12552     }
12553
12554  });
12555
12556 Roo.DomTemplate.from = function(el){
12557     el = Roo.getDom(el);
12558     return new Roo.Domtemplate(el.value || el.innerHTML);
12559 };/*
12560  * Based on:
12561  * Ext JS Library 1.1.1
12562  * Copyright(c) 2006-2007, Ext JS, LLC.
12563  *
12564  * Originally Released Under LGPL - original licence link has changed is not relivant.
12565  *
12566  * Fork - LGPL
12567  * <script type="text/javascript">
12568  */
12569
12570 /**
12571  * @class Roo.util.DelayedTask
12572  * Provides a convenient method of performing setTimeout where a new
12573  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12574  * You can use this class to buffer
12575  * the keypress events for a certain number of milliseconds, and perform only if they stop
12576  * for that amount of time.
12577  * @constructor The parameters to this constructor serve as defaults and are not required.
12578  * @param {Function} fn (optional) The default function to timeout
12579  * @param {Object} scope (optional) The default scope of that timeout
12580  * @param {Array} args (optional) The default Array of arguments
12581  */
12582 Roo.util.DelayedTask = function(fn, scope, args){
12583     var id = null, d, t;
12584
12585     var call = function(){
12586         var now = new Date().getTime();
12587         if(now - t >= d){
12588             clearInterval(id);
12589             id = null;
12590             fn.apply(scope, args || []);
12591         }
12592     };
12593     /**
12594      * Cancels any pending timeout and queues a new one
12595      * @param {Number} delay The milliseconds to delay
12596      * @param {Function} newFn (optional) Overrides function passed to constructor
12597      * @param {Object} newScope (optional) Overrides scope passed to constructor
12598      * @param {Array} newArgs (optional) Overrides args passed to constructor
12599      */
12600     this.delay = function(delay, newFn, newScope, newArgs){
12601         if(id && delay != d){
12602             this.cancel();
12603         }
12604         d = delay;
12605         t = new Date().getTime();
12606         fn = newFn || fn;
12607         scope = newScope || scope;
12608         args = newArgs || args;
12609         if(!id){
12610             id = setInterval(call, d);
12611         }
12612     };
12613
12614     /**
12615      * Cancel the last queued timeout
12616      */
12617     this.cancel = function(){
12618         if(id){
12619             clearInterval(id);
12620             id = null;
12621         }
12622     };
12623 };/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634  
12635 Roo.util.TaskRunner = function(interval){
12636     interval = interval || 10;
12637     var tasks = [], removeQueue = [];
12638     var id = 0;
12639     var running = false;
12640
12641     var stopThread = function(){
12642         running = false;
12643         clearInterval(id);
12644         id = 0;
12645     };
12646
12647     var startThread = function(){
12648         if(!running){
12649             running = true;
12650             id = setInterval(runTasks, interval);
12651         }
12652     };
12653
12654     var removeTask = function(task){
12655         removeQueue.push(task);
12656         if(task.onStop){
12657             task.onStop();
12658         }
12659     };
12660
12661     var runTasks = function(){
12662         if(removeQueue.length > 0){
12663             for(var i = 0, len = removeQueue.length; i < len; i++){
12664                 tasks.remove(removeQueue[i]);
12665             }
12666             removeQueue = [];
12667             if(tasks.length < 1){
12668                 stopThread();
12669                 return;
12670             }
12671         }
12672         var now = new Date().getTime();
12673         for(var i = 0, len = tasks.length; i < len; ++i){
12674             var t = tasks[i];
12675             var itime = now - t.taskRunTime;
12676             if(t.interval <= itime){
12677                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12678                 t.taskRunTime = now;
12679                 if(rt === false || t.taskRunCount === t.repeat){
12680                     removeTask(t);
12681                     return;
12682                 }
12683             }
12684             if(t.duration && t.duration <= (now - t.taskStartTime)){
12685                 removeTask(t);
12686             }
12687         }
12688     };
12689
12690     /**
12691      * Queues a new task.
12692      * @param {Object} task
12693      */
12694     this.start = function(task){
12695         tasks.push(task);
12696         task.taskStartTime = new Date().getTime();
12697         task.taskRunTime = 0;
12698         task.taskRunCount = 0;
12699         startThread();
12700         return task;
12701     };
12702
12703     this.stop = function(task){
12704         removeTask(task);
12705         return task;
12706     };
12707
12708     this.stopAll = function(){
12709         stopThread();
12710         for(var i = 0, len = tasks.length; i < len; i++){
12711             if(tasks[i].onStop){
12712                 tasks[i].onStop();
12713             }
12714         }
12715         tasks = [];
12716         removeQueue = [];
12717     };
12718 };
12719
12720 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730
12731  
12732 /**
12733  * @class Roo.util.MixedCollection
12734  * @extends Roo.util.Observable
12735  * A Collection class that maintains both numeric indexes and keys and exposes events.
12736  * @constructor
12737  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12738  * collection (defaults to false)
12739  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12740  * and return the key value for that item.  This is used when available to look up the key on items that
12741  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12742  * equivalent to providing an implementation for the {@link #getKey} method.
12743  */
12744 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12745     this.items = [];
12746     this.map = {};
12747     this.keys = [];
12748     this.length = 0;
12749     this.addEvents({
12750         /**
12751          * @event clear
12752          * Fires when the collection is cleared.
12753          */
12754         "clear" : true,
12755         /**
12756          * @event add
12757          * Fires when an item is added to the collection.
12758          * @param {Number} index The index at which the item was added.
12759          * @param {Object} o The item added.
12760          * @param {String} key The key associated with the added item.
12761          */
12762         "add" : true,
12763         /**
12764          * @event replace
12765          * Fires when an item is replaced in the collection.
12766          * @param {String} key he key associated with the new added.
12767          * @param {Object} old The item being replaced.
12768          * @param {Object} new The new item.
12769          */
12770         "replace" : true,
12771         /**
12772          * @event remove
12773          * Fires when an item is removed from the collection.
12774          * @param {Object} o The item being removed.
12775          * @param {String} key (optional) The key associated with the removed item.
12776          */
12777         "remove" : true,
12778         "sort" : true
12779     });
12780     this.allowFunctions = allowFunctions === true;
12781     if(keyFn){
12782         this.getKey = keyFn;
12783     }
12784     Roo.util.MixedCollection.superclass.constructor.call(this);
12785 };
12786
12787 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12788     allowFunctions : false,
12789     
12790 /**
12791  * Adds an item to the collection.
12792  * @param {String} key The key to associate with the item
12793  * @param {Object} o The item to add.
12794  * @return {Object} The item added.
12795  */
12796     add : function(key, o){
12797         if(arguments.length == 1){
12798             o = arguments[0];
12799             key = this.getKey(o);
12800         }
12801         if(typeof key == "undefined" || key === null){
12802             this.length++;
12803             this.items.push(o);
12804             this.keys.push(null);
12805         }else{
12806             var old = this.map[key];
12807             if(old){
12808                 return this.replace(key, o);
12809             }
12810             this.length++;
12811             this.items.push(o);
12812             this.map[key] = o;
12813             this.keys.push(key);
12814         }
12815         this.fireEvent("add", this.length-1, o, key);
12816         return o;
12817     },
12818        
12819 /**
12820   * MixedCollection has a generic way to fetch keys if you implement getKey.
12821 <pre><code>
12822 // normal way
12823 var mc = new Roo.util.MixedCollection();
12824 mc.add(someEl.dom.id, someEl);
12825 mc.add(otherEl.dom.id, otherEl);
12826 //and so on
12827
12828 // using getKey
12829 var mc = new Roo.util.MixedCollection();
12830 mc.getKey = function(el){
12831    return el.dom.id;
12832 };
12833 mc.add(someEl);
12834 mc.add(otherEl);
12835
12836 // or via the constructor
12837 var mc = new Roo.util.MixedCollection(false, function(el){
12838    return el.dom.id;
12839 });
12840 mc.add(someEl);
12841 mc.add(otherEl);
12842 </code></pre>
12843  * @param o {Object} The item for which to find the key.
12844  * @return {Object} The key for the passed item.
12845  */
12846     getKey : function(o){
12847          return o.id; 
12848     },
12849    
12850 /**
12851  * Replaces an item in the collection.
12852  * @param {String} key The key associated with the item to replace, or the item to replace.
12853  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12854  * @return {Object}  The new item.
12855  */
12856     replace : function(key, o){
12857         if(arguments.length == 1){
12858             o = arguments[0];
12859             key = this.getKey(o);
12860         }
12861         var old = this.item(key);
12862         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12863              return this.add(key, o);
12864         }
12865         var index = this.indexOfKey(key);
12866         this.items[index] = o;
12867         this.map[key] = o;
12868         this.fireEvent("replace", key, old, o);
12869         return o;
12870     },
12871    
12872 /**
12873  * Adds all elements of an Array or an Object to the collection.
12874  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12875  * an Array of values, each of which are added to the collection.
12876  */
12877     addAll : function(objs){
12878         if(arguments.length > 1 || objs instanceof Array){
12879             var args = arguments.length > 1 ? arguments : objs;
12880             for(var i = 0, len = args.length; i < len; i++){
12881                 this.add(args[i]);
12882             }
12883         }else{
12884             for(var key in objs){
12885                 if(this.allowFunctions || typeof objs[key] != "function"){
12886                     this.add(key, objs[key]);
12887                 }
12888             }
12889         }
12890     },
12891    
12892 /**
12893  * Executes the specified function once for every item in the collection, passing each
12894  * item as the first and only parameter. returning false from the function will stop the iteration.
12895  * @param {Function} fn The function to execute for each item.
12896  * @param {Object} scope (optional) The scope in which to execute the function.
12897  */
12898     each : function(fn, scope){
12899         var items = [].concat(this.items); // each safe for removal
12900         for(var i = 0, len = items.length; i < len; i++){
12901             if(fn.call(scope || items[i], items[i], i, len) === false){
12902                 break;
12903             }
12904         }
12905     },
12906    
12907 /**
12908  * Executes the specified function once for every key in the collection, passing each
12909  * key, and its associated item as the first two parameters.
12910  * @param {Function} fn The function to execute for each item.
12911  * @param {Object} scope (optional) The scope in which to execute the function.
12912  */
12913     eachKey : function(fn, scope){
12914         for(var i = 0, len = this.keys.length; i < len; i++){
12915             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12916         }
12917     },
12918    
12919 /**
12920  * Returns the first item in the collection which elicits a true return value from the
12921  * passed selection function.
12922  * @param {Function} fn The selection function to execute for each item.
12923  * @param {Object} scope (optional) The scope in which to execute the function.
12924  * @return {Object} The first item in the collection which returned true from the selection function.
12925  */
12926     find : function(fn, scope){
12927         for(var i = 0, len = this.items.length; i < len; i++){
12928             if(fn.call(scope || window, this.items[i], this.keys[i])){
12929                 return this.items[i];
12930             }
12931         }
12932         return null;
12933     },
12934    
12935 /**
12936  * Inserts an item at the specified index in the collection.
12937  * @param {Number} index The index to insert the item at.
12938  * @param {String} key The key to associate with the new item, or the item itself.
12939  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12940  * @return {Object} The item inserted.
12941  */
12942     insert : function(index, key, o){
12943         if(arguments.length == 2){
12944             o = arguments[1];
12945             key = this.getKey(o);
12946         }
12947         if(index >= this.length){
12948             return this.add(key, o);
12949         }
12950         this.length++;
12951         this.items.splice(index, 0, o);
12952         if(typeof key != "undefined" && key != null){
12953             this.map[key] = o;
12954         }
12955         this.keys.splice(index, 0, key);
12956         this.fireEvent("add", index, o, key);
12957         return o;
12958     },
12959    
12960 /**
12961  * Removed an item from the collection.
12962  * @param {Object} o The item to remove.
12963  * @return {Object} The item removed.
12964  */
12965     remove : function(o){
12966         return this.removeAt(this.indexOf(o));
12967     },
12968    
12969 /**
12970  * Remove an item from a specified index in the collection.
12971  * @param {Number} index The index within the collection of the item to remove.
12972  */
12973     removeAt : function(index){
12974         if(index < this.length && index >= 0){
12975             this.length--;
12976             var o = this.items[index];
12977             this.items.splice(index, 1);
12978             var key = this.keys[index];
12979             if(typeof key != "undefined"){
12980                 delete this.map[key];
12981             }
12982             this.keys.splice(index, 1);
12983             this.fireEvent("remove", o, key);
12984         }
12985     },
12986    
12987 /**
12988  * Removed an item associated with the passed key fom the collection.
12989  * @param {String} key The key of the item to remove.
12990  */
12991     removeKey : function(key){
12992         return this.removeAt(this.indexOfKey(key));
12993     },
12994    
12995 /**
12996  * Returns the number of items in the collection.
12997  * @return {Number} the number of items in the collection.
12998  */
12999     getCount : function(){
13000         return this.length; 
13001     },
13002    
13003 /**
13004  * Returns index within the collection of the passed Object.
13005  * @param {Object} o The item to find the index of.
13006  * @return {Number} index of the item.
13007  */
13008     indexOf : function(o){
13009         if(!this.items.indexOf){
13010             for(var i = 0, len = this.items.length; i < len; i++){
13011                 if(this.items[i] == o) return i;
13012             }
13013             return -1;
13014         }else{
13015             return this.items.indexOf(o);
13016         }
13017     },
13018    
13019 /**
13020  * Returns index within the collection of the passed key.
13021  * @param {String} key The key to find the index of.
13022  * @return {Number} index of the key.
13023  */
13024     indexOfKey : function(key){
13025         if(!this.keys.indexOf){
13026             for(var i = 0, len = this.keys.length; i < len; i++){
13027                 if(this.keys[i] == key) return i;
13028             }
13029             return -1;
13030         }else{
13031             return this.keys.indexOf(key);
13032         }
13033     },
13034    
13035 /**
13036  * Returns the item associated with the passed key OR index. Key has priority over index.
13037  * @param {String/Number} key The key or index of the item.
13038  * @return {Object} The item associated with the passed key.
13039  */
13040     item : function(key){
13041         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13042         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13043     },
13044     
13045 /**
13046  * Returns the item at the specified index.
13047  * @param {Number} index The index of the item.
13048  * @return {Object}
13049  */
13050     itemAt : function(index){
13051         return this.items[index];
13052     },
13053     
13054 /**
13055  * Returns the item associated with the passed key.
13056  * @param {String/Number} key The key of the item.
13057  * @return {Object} The item associated with the passed key.
13058  */
13059     key : function(key){
13060         return this.map[key];
13061     },
13062    
13063 /**
13064  * Returns true if the collection contains the passed Object as an item.
13065  * @param {Object} o  The Object to look for in the collection.
13066  * @return {Boolean} True if the collection contains the Object as an item.
13067  */
13068     contains : function(o){
13069         return this.indexOf(o) != -1;
13070     },
13071    
13072 /**
13073  * Returns true if the collection contains the passed Object as a key.
13074  * @param {String} key The key to look for in the collection.
13075  * @return {Boolean} True if the collection contains the Object as a key.
13076  */
13077     containsKey : function(key){
13078         return typeof this.map[key] != "undefined";
13079     },
13080    
13081 /**
13082  * Removes all items from the collection.
13083  */
13084     clear : function(){
13085         this.length = 0;
13086         this.items = [];
13087         this.keys = [];
13088         this.map = {};
13089         this.fireEvent("clear");
13090     },
13091    
13092 /**
13093  * Returns the first item in the collection.
13094  * @return {Object} the first item in the collection..
13095  */
13096     first : function(){
13097         return this.items[0]; 
13098     },
13099    
13100 /**
13101  * Returns the last item in the collection.
13102  * @return {Object} the last item in the collection..
13103  */
13104     last : function(){
13105         return this.items[this.length-1];   
13106     },
13107     
13108     _sort : function(property, dir, fn){
13109         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13110         fn = fn || function(a, b){
13111             return a-b;
13112         };
13113         var c = [], k = this.keys, items = this.items;
13114         for(var i = 0, len = items.length; i < len; i++){
13115             c[c.length] = {key: k[i], value: items[i], index: i};
13116         }
13117         c.sort(function(a, b){
13118             var v = fn(a[property], b[property]) * dsc;
13119             if(v == 0){
13120                 v = (a.index < b.index ? -1 : 1);
13121             }
13122             return v;
13123         });
13124         for(var i = 0, len = c.length; i < len; i++){
13125             items[i] = c[i].value;
13126             k[i] = c[i].key;
13127         }
13128         this.fireEvent("sort", this);
13129     },
13130     
13131     /**
13132      * Sorts this collection with the passed comparison function
13133      * @param {String} direction (optional) "ASC" or "DESC"
13134      * @param {Function} fn (optional) comparison function
13135      */
13136     sort : function(dir, fn){
13137         this._sort("value", dir, fn);
13138     },
13139     
13140     /**
13141      * Sorts this collection by keys
13142      * @param {String} direction (optional) "ASC" or "DESC"
13143      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13144      */
13145     keySort : function(dir, fn){
13146         this._sort("key", dir, fn || function(a, b){
13147             return String(a).toUpperCase()-String(b).toUpperCase();
13148         });
13149     },
13150     
13151     /**
13152      * Returns a range of items in this collection
13153      * @param {Number} startIndex (optional) defaults to 0
13154      * @param {Number} endIndex (optional) default to the last item
13155      * @return {Array} An array of items
13156      */
13157     getRange : function(start, end){
13158         var items = this.items;
13159         if(items.length < 1){
13160             return [];
13161         }
13162         start = start || 0;
13163         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13164         var r = [];
13165         if(start <= end){
13166             for(var i = start; i <= end; i++) {
13167                     r[r.length] = items[i];
13168             }
13169         }else{
13170             for(var i = start; i >= end; i--) {
13171                     r[r.length] = items[i];
13172             }
13173         }
13174         return r;
13175     },
13176         
13177     /**
13178      * Filter the <i>objects</i> in this collection by a specific property. 
13179      * Returns a new collection that has been filtered.
13180      * @param {String} property A property on your objects
13181      * @param {String/RegExp} value Either string that the property values 
13182      * should start with or a RegExp to test against the property
13183      * @return {MixedCollection} The new filtered collection
13184      */
13185     filter : function(property, value){
13186         if(!value.exec){ // not a regex
13187             value = String(value);
13188             if(value.length == 0){
13189                 return this.clone();
13190             }
13191             value = new RegExp("^" + Roo.escapeRe(value), "i");
13192         }
13193         return this.filterBy(function(o){
13194             return o && value.test(o[property]);
13195         });
13196         },
13197     
13198     /**
13199      * Filter by a function. * Returns a new collection that has been filtered.
13200      * The passed function will be called with each 
13201      * object in the collection. If the function returns true, the value is included 
13202      * otherwise it is filtered.
13203      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13204      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13205      * @return {MixedCollection} The new filtered collection
13206      */
13207     filterBy : function(fn, scope){
13208         var r = new Roo.util.MixedCollection();
13209         r.getKey = this.getKey;
13210         var k = this.keys, it = this.items;
13211         for(var i = 0, len = it.length; i < len; i++){
13212             if(fn.call(scope||this, it[i], k[i])){
13213                                 r.add(k[i], it[i]);
13214                         }
13215         }
13216         return r;
13217     },
13218     
13219     /**
13220      * Creates a duplicate of this collection
13221      * @return {MixedCollection}
13222      */
13223     clone : function(){
13224         var r = new Roo.util.MixedCollection();
13225         var k = this.keys, it = this.items;
13226         for(var i = 0, len = it.length; i < len; i++){
13227             r.add(k[i], it[i]);
13228         }
13229         r.getKey = this.getKey;
13230         return r;
13231     }
13232 });
13233 /**
13234  * Returns the item associated with the passed key or index.
13235  * @method
13236  * @param {String/Number} key The key or index of the item.
13237  * @return {Object} The item associated with the passed key.
13238  */
13239 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13240  * Based on:
13241  * Ext JS Library 1.1.1
13242  * Copyright(c) 2006-2007, Ext JS, LLC.
13243  *
13244  * Originally Released Under LGPL - original licence link has changed is not relivant.
13245  *
13246  * Fork - LGPL
13247  * <script type="text/javascript">
13248  */
13249 /**
13250  * @class Roo.util.JSON
13251  * Modified version of Douglas Crockford"s json.js that doesn"t
13252  * mess with the Object prototype 
13253  * http://www.json.org/js.html
13254  * @singleton
13255  */
13256 Roo.util.JSON = new (function(){
13257     var useHasOwn = {}.hasOwnProperty ? true : false;
13258     
13259     // crashes Safari in some instances
13260     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13261     
13262     var pad = function(n) {
13263         return n < 10 ? "0" + n : n;
13264     };
13265     
13266     var m = {
13267         "\b": '\\b',
13268         "\t": '\\t',
13269         "\n": '\\n',
13270         "\f": '\\f',
13271         "\r": '\\r',
13272         '"' : '\\"',
13273         "\\": '\\\\'
13274     };
13275
13276     var encodeString = function(s){
13277         if (/["\\\x00-\x1f]/.test(s)) {
13278             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13279                 var c = m[b];
13280                 if(c){
13281                     return c;
13282                 }
13283                 c = b.charCodeAt();
13284                 return "\\u00" +
13285                     Math.floor(c / 16).toString(16) +
13286                     (c % 16).toString(16);
13287             }) + '"';
13288         }
13289         return '"' + s + '"';
13290     };
13291     
13292     var encodeArray = function(o){
13293         var a = ["["], b, i, l = o.length, v;
13294             for (i = 0; i < l; i += 1) {
13295                 v = o[i];
13296                 switch (typeof v) {
13297                     case "undefined":
13298                     case "function":
13299                     case "unknown":
13300                         break;
13301                     default:
13302                         if (b) {
13303                             a.push(',');
13304                         }
13305                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13306                         b = true;
13307                 }
13308             }
13309             a.push("]");
13310             return a.join("");
13311     };
13312     
13313     var encodeDate = function(o){
13314         return '"' + o.getFullYear() + "-" +
13315                 pad(o.getMonth() + 1) + "-" +
13316                 pad(o.getDate()) + "T" +
13317                 pad(o.getHours()) + ":" +
13318                 pad(o.getMinutes()) + ":" +
13319                 pad(o.getSeconds()) + '"';
13320     };
13321     
13322     /**
13323      * Encodes an Object, Array or other value
13324      * @param {Mixed} o The variable to encode
13325      * @return {String} The JSON string
13326      */
13327     this.encode = function(o)
13328     {
13329         // should this be extended to fully wrap stringify..
13330         
13331         if(typeof o == "undefined" || o === null){
13332             return "null";
13333         }else if(o instanceof Array){
13334             return encodeArray(o);
13335         }else if(o instanceof Date){
13336             return encodeDate(o);
13337         }else if(typeof o == "string"){
13338             return encodeString(o);
13339         }else if(typeof o == "number"){
13340             return isFinite(o) ? String(o) : "null";
13341         }else if(typeof o == "boolean"){
13342             return String(o);
13343         }else {
13344             var a = ["{"], b, i, v;
13345             for (i in o) {
13346                 if(!useHasOwn || o.hasOwnProperty(i)) {
13347                     v = o[i];
13348                     switch (typeof v) {
13349                     case "undefined":
13350                     case "function":
13351                     case "unknown":
13352                         break;
13353                     default:
13354                         if(b){
13355                             a.push(',');
13356                         }
13357                         a.push(this.encode(i), ":",
13358                                 v === null ? "null" : this.encode(v));
13359                         b = true;
13360                     }
13361                 }
13362             }
13363             a.push("}");
13364             return a.join("");
13365         }
13366     };
13367     
13368     /**
13369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13370      * @param {String} json The JSON string
13371      * @return {Object} The resulting object
13372      */
13373     this.decode = function(json){
13374         
13375         return  /** eval:var:json */ eval("(" + json + ')');
13376     };
13377 })();
13378 /** 
13379  * Shorthand for {@link Roo.util.JSON#encode}
13380  * @member Roo encode 
13381  * @method */
13382 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13383 /** 
13384  * Shorthand for {@link Roo.util.JSON#decode}
13385  * @member Roo decode 
13386  * @method */
13387 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13388 /*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398  
13399 /**
13400  * @class Roo.util.Format
13401  * Reusable data formatting functions
13402  * @singleton
13403  */
13404 Roo.util.Format = function(){
13405     var trimRe = /^\s+|\s+$/g;
13406     return {
13407         /**
13408          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13409          * @param {String} value The string to truncate
13410          * @param {Number} length The maximum length to allow before truncating
13411          * @return {String} The converted text
13412          */
13413         ellipsis : function(value, len){
13414             if(value && value.length > len){
13415                 return value.substr(0, len-3)+"...";
13416             }
13417             return value;
13418         },
13419
13420         /**
13421          * Checks a reference and converts it to empty string if it is undefined
13422          * @param {Mixed} value Reference to check
13423          * @return {Mixed} Empty string if converted, otherwise the original value
13424          */
13425         undef : function(value){
13426             return typeof value != "undefined" ? value : "";
13427         },
13428
13429         /**
13430          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13431          * @param {String} value The string to encode
13432          * @return {String} The encoded text
13433          */
13434         htmlEncode : function(value){
13435             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13436         },
13437
13438         /**
13439          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13440          * @param {String} value The string to decode
13441          * @return {String} The decoded text
13442          */
13443         htmlDecode : function(value){
13444             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13445         },
13446
13447         /**
13448          * Trims any whitespace from either side of a string
13449          * @param {String} value The text to trim
13450          * @return {String} The trimmed text
13451          */
13452         trim : function(value){
13453             return String(value).replace(trimRe, "");
13454         },
13455
13456         /**
13457          * Returns a substring from within an original string
13458          * @param {String} value The original text
13459          * @param {Number} start The start index of the substring
13460          * @param {Number} length The length of the substring
13461          * @return {String} The substring
13462          */
13463         substr : function(value, start, length){
13464             return String(value).substr(start, length);
13465         },
13466
13467         /**
13468          * Converts a string to all lower case letters
13469          * @param {String} value The text to convert
13470          * @return {String} The converted text
13471          */
13472         lowercase : function(value){
13473             return String(value).toLowerCase();
13474         },
13475
13476         /**
13477          * Converts a string to all upper case letters
13478          * @param {String} value The text to convert
13479          * @return {String} The converted text
13480          */
13481         uppercase : function(value){
13482             return String(value).toUpperCase();
13483         },
13484
13485         /**
13486          * Converts the first character only of a string to upper case
13487          * @param {String} value The text to convert
13488          * @return {String} The converted text
13489          */
13490         capitalize : function(value){
13491             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13492         },
13493
13494         // private
13495         call : function(value, fn){
13496             if(arguments.length > 2){
13497                 var args = Array.prototype.slice.call(arguments, 2);
13498                 args.unshift(value);
13499                  
13500                 return /** eval:var:value */  eval(fn).apply(window, args);
13501             }else{
13502                 /** eval:var:value */
13503                 return /** eval:var:value */ eval(fn).call(window, value);
13504             }
13505         },
13506
13507        
13508         /**
13509          * safer version of Math.toFixed..??/
13510          * @param {Number/String} value The numeric value to format
13511          * @param {Number/String} value Decimal places 
13512          * @return {String} The formatted currency string
13513          */
13514         toFixed : function(v, n)
13515         {
13516             // why not use to fixed - precision is buggered???
13517             if (!n) {
13518                 return Math.round(v-0);
13519             }
13520             var fact = Math.pow(10,n+1);
13521             v = (Math.round((v-0)*fact))/fact;
13522             var z = (''+fact).substring(2);
13523             if (v == Math.floor(v)) {
13524                 return Math.floor(v) + '.' + z;
13525             }
13526             
13527             // now just padd decimals..
13528             var ps = String(v).split('.');
13529             var fd = (ps[1] + z);
13530             var r = fd.substring(0,n); 
13531             var rm = fd.substring(n); 
13532             if (rm < 5) {
13533                 return ps[0] + '.' + r;
13534             }
13535             r*=1; // turn it into a number;
13536             r++;
13537             if (String(r).length != n) {
13538                 ps[0]*=1;
13539                 ps[0]++;
13540                 r = String(r).substring(1); // chop the end off.
13541             }
13542             
13543             return ps[0] + '.' + r;
13544              
13545         },
13546         
13547         /**
13548          * Format a number as US currency
13549          * @param {Number/String} value The numeric value to format
13550          * @return {String} The formatted currency string
13551          */
13552         usMoney : function(v){
13553             return '$' + Roo.util.Format.number(v);
13554         },
13555         
13556         /**
13557          * Format a number
13558          * eventually this should probably emulate php's number_format
13559          * @param {Number/String} value The numeric value to format
13560          * @param {Number} decimals number of decimal places
13561          * @return {String} The formatted currency string
13562          */
13563         number : function(v,decimals)
13564         {
13565             // multiply and round.
13566             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13567             var mul = Math.pow(10, decimals);
13568             var zero = String(mul).substring(1);
13569             v = (Math.round((v-0)*mul))/mul;
13570             
13571             // if it's '0' number.. then
13572             
13573             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13574             v = String(v);
13575             var ps = v.split('.');
13576             var whole = ps[0];
13577             
13578             
13579             var r = /(\d+)(\d{3})/;
13580             // add comma's
13581             while (r.test(whole)) {
13582                 whole = whole.replace(r, '$1' + ',' + '$2');
13583             }
13584             
13585             
13586             var sub = ps[1] ?
13587                     // has decimals..
13588                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13589                     // does not have decimals
13590                     (decimals ? ('.' + zero) : '');
13591             
13592             
13593             return whole + sub ;
13594         },
13595         
13596         /**
13597          * Parse a value into a formatted date using the specified format pattern.
13598          * @param {Mixed} value The value to format
13599          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13600          * @return {String} The formatted date string
13601          */
13602         date : function(v, format){
13603             if(!v){
13604                 return "";
13605             }
13606             if(!(v instanceof Date)){
13607                 v = new Date(Date.parse(v));
13608             }
13609             return v.dateFormat(format || "m/d/Y");
13610         },
13611
13612         /**
13613          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13614          * @param {String} format Any valid date format string
13615          * @return {Function} The date formatting function
13616          */
13617         dateRenderer : function(format){
13618             return function(v){
13619                 return Roo.util.Format.date(v, format);  
13620             };
13621         },
13622
13623         // private
13624         stripTagsRE : /<\/?[^>]+>/gi,
13625         
13626         /**
13627          * Strips all HTML tags
13628          * @param {Mixed} value The text from which to strip tags
13629          * @return {String} The stripped text
13630          */
13631         stripTags : function(v){
13632             return !v ? v : String(v).replace(this.stripTagsRE, "");
13633         }
13634     };
13635 }();/*
13636  * Based on:
13637  * Ext JS Library 1.1.1
13638  * Copyright(c) 2006-2007, Ext JS, LLC.
13639  *
13640  * Originally Released Under LGPL - original licence link has changed is not relivant.
13641  *
13642  * Fork - LGPL
13643  * <script type="text/javascript">
13644  */
13645
13646
13647  
13648
13649 /**
13650  * @class Roo.MasterTemplate
13651  * @extends Roo.Template
13652  * Provides a template that can have child templates. The syntax is:
13653 <pre><code>
13654 var t = new Roo.MasterTemplate(
13655         '&lt;select name="{name}"&gt;',
13656                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13657         '&lt;/select&gt;'
13658 );
13659 t.add('options', {value: 'foo', text: 'bar'});
13660 // or you can add multiple child elements in one shot
13661 t.addAll('options', [
13662     {value: 'foo', text: 'bar'},
13663     {value: 'foo2', text: 'bar2'},
13664     {value: 'foo3', text: 'bar3'}
13665 ]);
13666 // then append, applying the master template values
13667 t.append('my-form', {name: 'my-select'});
13668 </code></pre>
13669 * A name attribute for the child template is not required if you have only one child
13670 * template or you want to refer to them by index.
13671  */
13672 Roo.MasterTemplate = function(){
13673     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13674     this.originalHtml = this.html;
13675     var st = {};
13676     var m, re = this.subTemplateRe;
13677     re.lastIndex = 0;
13678     var subIndex = 0;
13679     while(m = re.exec(this.html)){
13680         var name = m[1], content = m[2];
13681         st[subIndex] = {
13682             name: name,
13683             index: subIndex,
13684             buffer: [],
13685             tpl : new Roo.Template(content)
13686         };
13687         if(name){
13688             st[name] = st[subIndex];
13689         }
13690         st[subIndex].tpl.compile();
13691         st[subIndex].tpl.call = this.call.createDelegate(this);
13692         subIndex++;
13693     }
13694     this.subCount = subIndex;
13695     this.subs = st;
13696 };
13697 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13698     /**
13699     * The regular expression used to match sub templates
13700     * @type RegExp
13701     * @property
13702     */
13703     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13704
13705     /**
13706      * Applies the passed values to a child template.
13707      * @param {String/Number} name (optional) The name or index of the child template
13708      * @param {Array/Object} values The values to be applied to the template
13709      * @return {MasterTemplate} this
13710      */
13711      add : function(name, values){
13712         if(arguments.length == 1){
13713             values = arguments[0];
13714             name = 0;
13715         }
13716         var s = this.subs[name];
13717         s.buffer[s.buffer.length] = s.tpl.apply(values);
13718         return this;
13719     },
13720
13721     /**
13722      * Applies all the passed values to a child template.
13723      * @param {String/Number} name (optional) The name or index of the child template
13724      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13725      * @param {Boolean} reset (optional) True to reset the template first
13726      * @return {MasterTemplate} this
13727      */
13728     fill : function(name, values, reset){
13729         var a = arguments;
13730         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13731             values = a[0];
13732             name = 0;
13733             reset = a[1];
13734         }
13735         if(reset){
13736             this.reset();
13737         }
13738         for(var i = 0, len = values.length; i < len; i++){
13739             this.add(name, values[i]);
13740         }
13741         return this;
13742     },
13743
13744     /**
13745      * Resets the template for reuse
13746      * @return {MasterTemplate} this
13747      */
13748      reset : function(){
13749         var s = this.subs;
13750         for(var i = 0; i < this.subCount; i++){
13751             s[i].buffer = [];
13752         }
13753         return this;
13754     },
13755
13756     applyTemplate : function(values){
13757         var s = this.subs;
13758         var replaceIndex = -1;
13759         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13760             return s[++replaceIndex].buffer.join("");
13761         });
13762         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13763     },
13764
13765     apply : function(){
13766         return this.applyTemplate.apply(this, arguments);
13767     },
13768
13769     compile : function(){return this;}
13770 });
13771
13772 /**
13773  * Alias for fill().
13774  * @method
13775  */
13776 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13777  /**
13778  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13779  * var tpl = Roo.MasterTemplate.from('element-id');
13780  * @param {String/HTMLElement} el
13781  * @param {Object} config
13782  * @static
13783  */
13784 Roo.MasterTemplate.from = function(el, config){
13785     el = Roo.getDom(el);
13786     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13787 };/*
13788  * Based on:
13789  * Ext JS Library 1.1.1
13790  * Copyright(c) 2006-2007, Ext JS, LLC.
13791  *
13792  * Originally Released Under LGPL - original licence link has changed is not relivant.
13793  *
13794  * Fork - LGPL
13795  * <script type="text/javascript">
13796  */
13797
13798  
13799 /**
13800  * @class Roo.util.CSS
13801  * Utility class for manipulating CSS rules
13802  * @singleton
13803  */
13804 Roo.util.CSS = function(){
13805         var rules = null;
13806         var doc = document;
13807
13808     var camelRe = /(-[a-z])/gi;
13809     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13810
13811    return {
13812    /**
13813     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13814     * tag and appended to the HEAD of the document.
13815     * @param {String|Object} cssText The text containing the css rules
13816     * @param {String} id An id to add to the stylesheet for later removal
13817     * @return {StyleSheet}
13818     */
13819     createStyleSheet : function(cssText, id){
13820         var ss;
13821         var head = doc.getElementsByTagName("head")[0];
13822         var nrules = doc.createElement("style");
13823         nrules.setAttribute("type", "text/css");
13824         if(id){
13825             nrules.setAttribute("id", id);
13826         }
13827         if (typeof(cssText) != 'string') {
13828             // support object maps..
13829             // not sure if this a good idea.. 
13830             // perhaps it should be merged with the general css handling
13831             // and handle js style props.
13832             var cssTextNew = [];
13833             for(var n in cssText) {
13834                 var citems = [];
13835                 for(var k in cssText[n]) {
13836                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13837                 }
13838                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13839                 
13840             }
13841             cssText = cssTextNew.join("\n");
13842             
13843         }
13844        
13845        
13846        if(Roo.isIE){
13847            head.appendChild(nrules);
13848            ss = nrules.styleSheet;
13849            ss.cssText = cssText;
13850        }else{
13851            try{
13852                 nrules.appendChild(doc.createTextNode(cssText));
13853            }catch(e){
13854                nrules.cssText = cssText; 
13855            }
13856            head.appendChild(nrules);
13857            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13858        }
13859        this.cacheStyleSheet(ss);
13860        return ss;
13861    },
13862
13863    /**
13864     * Removes a style or link tag by id
13865     * @param {String} id The id of the tag
13866     */
13867    removeStyleSheet : function(id){
13868        var existing = doc.getElementById(id);
13869        if(existing){
13870            existing.parentNode.removeChild(existing);
13871        }
13872    },
13873
13874    /**
13875     * Dynamically swaps an existing stylesheet reference for a new one
13876     * @param {String} id The id of an existing link tag to remove
13877     * @param {String} url The href of the new stylesheet to include
13878     */
13879    swapStyleSheet : function(id, url){
13880        this.removeStyleSheet(id);
13881        var ss = doc.createElement("link");
13882        ss.setAttribute("rel", "stylesheet");
13883        ss.setAttribute("type", "text/css");
13884        ss.setAttribute("id", id);
13885        ss.setAttribute("href", url);
13886        doc.getElementsByTagName("head")[0].appendChild(ss);
13887    },
13888    
13889    /**
13890     * Refresh the rule cache if you have dynamically added stylesheets
13891     * @return {Object} An object (hash) of rules indexed by selector
13892     */
13893    refreshCache : function(){
13894        return this.getRules(true);
13895    },
13896
13897    // private
13898    cacheStyleSheet : function(stylesheet){
13899        if(!rules){
13900            rules = {};
13901        }
13902        try{// try catch for cross domain access issue
13903            var ssRules = stylesheet.cssRules || stylesheet.rules;
13904            for(var j = ssRules.length-1; j >= 0; --j){
13905                rules[ssRules[j].selectorText] = ssRules[j];
13906            }
13907        }catch(e){}
13908    },
13909    
13910    /**
13911     * Gets all css rules for the document
13912     * @param {Boolean} refreshCache true to refresh the internal cache
13913     * @return {Object} An object (hash) of rules indexed by selector
13914     */
13915    getRules : function(refreshCache){
13916                 if(rules == null || refreshCache){
13917                         rules = {};
13918                         var ds = doc.styleSheets;
13919                         for(var i =0, len = ds.length; i < len; i++){
13920                             try{
13921                         this.cacheStyleSheet(ds[i]);
13922                     }catch(e){} 
13923                 }
13924                 }
13925                 return rules;
13926         },
13927         
13928         /**
13929     * Gets an an individual CSS rule by selector(s)
13930     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13931     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13932     * @return {CSSRule} The CSS rule or null if one is not found
13933     */
13934    getRule : function(selector, refreshCache){
13935                 var rs = this.getRules(refreshCache);
13936                 if(!(selector instanceof Array)){
13937                     return rs[selector];
13938                 }
13939                 for(var i = 0; i < selector.length; i++){
13940                         if(rs[selector[i]]){
13941                                 return rs[selector[i]];
13942                         }
13943                 }
13944                 return null;
13945         },
13946         
13947         
13948         /**
13949     * Updates a rule property
13950     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13951     * @param {String} property The css property
13952     * @param {String} value The new value for the property
13953     * @return {Boolean} true If a rule was found and updated
13954     */
13955    updateRule : function(selector, property, value){
13956                 if(!(selector instanceof Array)){
13957                         var rule = this.getRule(selector);
13958                         if(rule){
13959                                 rule.style[property.replace(camelRe, camelFn)] = value;
13960                                 return true;
13961                         }
13962                 }else{
13963                         for(var i = 0; i < selector.length; i++){
13964                                 if(this.updateRule(selector[i], property, value)){
13965                                         return true;
13966                                 }
13967                         }
13968                 }
13969                 return false;
13970         }
13971    };   
13972 }();/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982
13983  
13984
13985 /**
13986  * @class Roo.util.ClickRepeater
13987  * @extends Roo.util.Observable
13988  * 
13989  * A wrapper class which can be applied to any element. Fires a "click" event while the
13990  * mouse is pressed. The interval between firings may be specified in the config but
13991  * defaults to 10 milliseconds.
13992  * 
13993  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13994  * 
13995  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13996  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13997  * Similar to an autorepeat key delay.
13998  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13999  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14000  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14001  *           "interval" and "delay" are ignored. "immediate" is honored.
14002  * @cfg {Boolean} preventDefault True to prevent the default click event
14003  * @cfg {Boolean} stopDefault True to stop the default click event
14004  * 
14005  * @history
14006  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14007  *     2007-02-02 jvs Renamed to ClickRepeater
14008  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14009  *
14010  *  @constructor
14011  * @param {String/HTMLElement/Element} el The element to listen on
14012  * @param {Object} config
14013  **/
14014 Roo.util.ClickRepeater = function(el, config)
14015 {
14016     this.el = Roo.get(el);
14017     this.el.unselectable();
14018
14019     Roo.apply(this, config);
14020
14021     this.addEvents({
14022     /**
14023      * @event mousedown
14024      * Fires when the mouse button is depressed.
14025      * @param {Roo.util.ClickRepeater} this
14026      */
14027         "mousedown" : true,
14028     /**
14029      * @event click
14030      * Fires on a specified interval during the time the element is pressed.
14031      * @param {Roo.util.ClickRepeater} this
14032      */
14033         "click" : true,
14034     /**
14035      * @event mouseup
14036      * Fires when the mouse key is released.
14037      * @param {Roo.util.ClickRepeater} this
14038      */
14039         "mouseup" : true
14040     });
14041
14042     this.el.on("mousedown", this.handleMouseDown, this);
14043     if(this.preventDefault || this.stopDefault){
14044         this.el.on("click", function(e){
14045             if(this.preventDefault){
14046                 e.preventDefault();
14047             }
14048             if(this.stopDefault){
14049                 e.stopEvent();
14050             }
14051         }, this);
14052     }
14053
14054     // allow inline handler
14055     if(this.handler){
14056         this.on("click", this.handler,  this.scope || this);
14057     }
14058
14059     Roo.util.ClickRepeater.superclass.constructor.call(this);
14060 };
14061
14062 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14063     interval : 20,
14064     delay: 250,
14065     preventDefault : true,
14066     stopDefault : false,
14067     timer : 0,
14068
14069     // private
14070     handleMouseDown : function(){
14071         clearTimeout(this.timer);
14072         this.el.blur();
14073         if(this.pressClass){
14074             this.el.addClass(this.pressClass);
14075         }
14076         this.mousedownTime = new Date();
14077
14078         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14079         this.el.on("mouseout", this.handleMouseOut, this);
14080
14081         this.fireEvent("mousedown", this);
14082         this.fireEvent("click", this);
14083         
14084         this.timer = this.click.defer(this.delay || this.interval, this);
14085     },
14086
14087     // private
14088     click : function(){
14089         this.fireEvent("click", this);
14090         this.timer = this.click.defer(this.getInterval(), this);
14091     },
14092
14093     // private
14094     getInterval: function(){
14095         if(!this.accelerate){
14096             return this.interval;
14097         }
14098         var pressTime = this.mousedownTime.getElapsed();
14099         if(pressTime < 500){
14100             return 400;
14101         }else if(pressTime < 1700){
14102             return 320;
14103         }else if(pressTime < 2600){
14104             return 250;
14105         }else if(pressTime < 3500){
14106             return 180;
14107         }else if(pressTime < 4400){
14108             return 140;
14109         }else if(pressTime < 5300){
14110             return 80;
14111         }else if(pressTime < 6200){
14112             return 50;
14113         }else{
14114             return 10;
14115         }
14116     },
14117
14118     // private
14119     handleMouseOut : function(){
14120         clearTimeout(this.timer);
14121         if(this.pressClass){
14122             this.el.removeClass(this.pressClass);
14123         }
14124         this.el.on("mouseover", this.handleMouseReturn, this);
14125     },
14126
14127     // private
14128     handleMouseReturn : function(){
14129         this.el.un("mouseover", this.handleMouseReturn);
14130         if(this.pressClass){
14131             this.el.addClass(this.pressClass);
14132         }
14133         this.click();
14134     },
14135
14136     // private
14137     handleMouseUp : function(){
14138         clearTimeout(this.timer);
14139         this.el.un("mouseover", this.handleMouseReturn);
14140         this.el.un("mouseout", this.handleMouseOut);
14141         Roo.get(document).un("mouseup", this.handleMouseUp);
14142         this.el.removeClass(this.pressClass);
14143         this.fireEvent("mouseup", this);
14144     }
14145 });/*
14146  * Based on:
14147  * Ext JS Library 1.1.1
14148  * Copyright(c) 2006-2007, Ext JS, LLC.
14149  *
14150  * Originally Released Under LGPL - original licence link has changed is not relivant.
14151  *
14152  * Fork - LGPL
14153  * <script type="text/javascript">
14154  */
14155
14156  
14157 /**
14158  * @class Roo.KeyNav
14159  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14160  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14161  * way to implement custom navigation schemes for any UI component.</p>
14162  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14163  * pageUp, pageDown, del, home, end.  Usage:</p>
14164  <pre><code>
14165 var nav = new Roo.KeyNav("my-element", {
14166     "left" : function(e){
14167         this.moveLeft(e.ctrlKey);
14168     },
14169     "right" : function(e){
14170         this.moveRight(e.ctrlKey);
14171     },
14172     "enter" : function(e){
14173         this.save();
14174     },
14175     scope : this
14176 });
14177 </code></pre>
14178  * @constructor
14179  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14180  * @param {Object} config The config
14181  */
14182 Roo.KeyNav = function(el, config){
14183     this.el = Roo.get(el);
14184     Roo.apply(this, config);
14185     if(!this.disabled){
14186         this.disabled = true;
14187         this.enable();
14188     }
14189 };
14190
14191 Roo.KeyNav.prototype = {
14192     /**
14193      * @cfg {Boolean} disabled
14194      * True to disable this KeyNav instance (defaults to false)
14195      */
14196     disabled : false,
14197     /**
14198      * @cfg {String} defaultEventAction
14199      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14200      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14201      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14202      */
14203     defaultEventAction: "stopEvent",
14204     /**
14205      * @cfg {Boolean} forceKeyDown
14206      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14207      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14208      * handle keydown instead of keypress.
14209      */
14210     forceKeyDown : false,
14211
14212     // private
14213     prepareEvent : function(e){
14214         var k = e.getKey();
14215         var h = this.keyToHandler[k];
14216         //if(h && this[h]){
14217         //    e.stopPropagation();
14218         //}
14219         if(Roo.isSafari && h && k >= 37 && k <= 40){
14220             e.stopEvent();
14221         }
14222     },
14223
14224     // private
14225     relay : function(e){
14226         var k = e.getKey();
14227         var h = this.keyToHandler[k];
14228         if(h && this[h]){
14229             if(this.doRelay(e, this[h], h) !== true){
14230                 e[this.defaultEventAction]();
14231             }
14232         }
14233     },
14234
14235     // private
14236     doRelay : function(e, h, hname){
14237         return h.call(this.scope || this, e);
14238     },
14239
14240     // possible handlers
14241     enter : false,
14242     left : false,
14243     right : false,
14244     up : false,
14245     down : false,
14246     tab : false,
14247     esc : false,
14248     pageUp : false,
14249     pageDown : false,
14250     del : false,
14251     home : false,
14252     end : false,
14253
14254     // quick lookup hash
14255     keyToHandler : {
14256         37 : "left",
14257         39 : "right",
14258         38 : "up",
14259         40 : "down",
14260         33 : "pageUp",
14261         34 : "pageDown",
14262         46 : "del",
14263         36 : "home",
14264         35 : "end",
14265         13 : "enter",
14266         27 : "esc",
14267         9  : "tab"
14268     },
14269
14270         /**
14271          * Enable this KeyNav
14272          */
14273         enable: function(){
14274                 if(this.disabled){
14275             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14276             // the EventObject will normalize Safari automatically
14277             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14278                 this.el.on("keydown", this.relay,  this);
14279             }else{
14280                 this.el.on("keydown", this.prepareEvent,  this);
14281                 this.el.on("keypress", this.relay,  this);
14282             }
14283                     this.disabled = false;
14284                 }
14285         },
14286
14287         /**
14288          * Disable this KeyNav
14289          */
14290         disable: function(){
14291                 if(!this.disabled){
14292                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14293                 this.el.un("keydown", this.relay);
14294             }else{
14295                 this.el.un("keydown", this.prepareEvent);
14296                 this.el.un("keypress", this.relay);
14297             }
14298                     this.disabled = true;
14299                 }
14300         }
14301 };/*
14302  * Based on:
14303  * Ext JS Library 1.1.1
14304  * Copyright(c) 2006-2007, Ext JS, LLC.
14305  *
14306  * Originally Released Under LGPL - original licence link has changed is not relivant.
14307  *
14308  * Fork - LGPL
14309  * <script type="text/javascript">
14310  */
14311
14312  
14313 /**
14314  * @class Roo.KeyMap
14315  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14316  * The constructor accepts the same config object as defined by {@link #addBinding}.
14317  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14318  * combination it will call the function with this signature (if the match is a multi-key
14319  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14320  * A KeyMap can also handle a string representation of keys.<br />
14321  * Usage:
14322  <pre><code>
14323 // map one key by key code
14324 var map = new Roo.KeyMap("my-element", {
14325     key: 13, // or Roo.EventObject.ENTER
14326     fn: myHandler,
14327     scope: myObject
14328 });
14329
14330 // map multiple keys to one action by string
14331 var map = new Roo.KeyMap("my-element", {
14332     key: "a\r\n\t",
14333     fn: myHandler,
14334     scope: myObject
14335 });
14336
14337 // map multiple keys to multiple actions by strings and array of codes
14338 var map = new Roo.KeyMap("my-element", [
14339     {
14340         key: [10,13],
14341         fn: function(){ alert("Return was pressed"); }
14342     }, {
14343         key: "abc",
14344         fn: function(){ alert('a, b or c was pressed'); }
14345     }, {
14346         key: "\t",
14347         ctrl:true,
14348         shift:true,
14349         fn: function(){ alert('Control + shift + tab was pressed.'); }
14350     }
14351 ]);
14352 </code></pre>
14353  * <b>Note: A KeyMap starts enabled</b>
14354  * @constructor
14355  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14356  * @param {Object} config The config (see {@link #addBinding})
14357  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14358  */
14359 Roo.KeyMap = function(el, config, eventName){
14360     this.el  = Roo.get(el);
14361     this.eventName = eventName || "keydown";
14362     this.bindings = [];
14363     if(config){
14364         this.addBinding(config);
14365     }
14366     this.enable();
14367 };
14368
14369 Roo.KeyMap.prototype = {
14370     /**
14371      * True to stop the event from bubbling and prevent the default browser action if the
14372      * key was handled by the KeyMap (defaults to false)
14373      * @type Boolean
14374      */
14375     stopEvent : false,
14376
14377     /**
14378      * Add a new binding to this KeyMap. The following config object properties are supported:
14379      * <pre>
14380 Property    Type             Description
14381 ----------  ---------------  ----------------------------------------------------------------------
14382 key         String/Array     A single keycode or an array of keycodes to handle
14383 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14384 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14385 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14386 fn          Function         The function to call when KeyMap finds the expected key combination
14387 scope       Object           The scope of the callback function
14388 </pre>
14389      *
14390      * Usage:
14391      * <pre><code>
14392 // Create a KeyMap
14393 var map = new Roo.KeyMap(document, {
14394     key: Roo.EventObject.ENTER,
14395     fn: handleKey,
14396     scope: this
14397 });
14398
14399 //Add a new binding to the existing KeyMap later
14400 map.addBinding({
14401     key: 'abc',
14402     shift: true,
14403     fn: handleKey,
14404     scope: this
14405 });
14406 </code></pre>
14407      * @param {Object/Array} config A single KeyMap config or an array of configs
14408      */
14409         addBinding : function(config){
14410         if(config instanceof Array){
14411             for(var i = 0, len = config.length; i < len; i++){
14412                 this.addBinding(config[i]);
14413             }
14414             return;
14415         }
14416         var keyCode = config.key,
14417             shift = config.shift, 
14418             ctrl = config.ctrl, 
14419             alt = config.alt,
14420             fn = config.fn,
14421             scope = config.scope;
14422         if(typeof keyCode == "string"){
14423             var ks = [];
14424             var keyString = keyCode.toUpperCase();
14425             for(var j = 0, len = keyString.length; j < len; j++){
14426                 ks.push(keyString.charCodeAt(j));
14427             }
14428             keyCode = ks;
14429         }
14430         var keyArray = keyCode instanceof Array;
14431         var handler = function(e){
14432             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14433                 var k = e.getKey();
14434                 if(keyArray){
14435                     for(var i = 0, len = keyCode.length; i < len; i++){
14436                         if(keyCode[i] == k){
14437                           if(this.stopEvent){
14438                               e.stopEvent();
14439                           }
14440                           fn.call(scope || window, k, e);
14441                           return;
14442                         }
14443                     }
14444                 }else{
14445                     if(k == keyCode){
14446                         if(this.stopEvent){
14447                            e.stopEvent();
14448                         }
14449                         fn.call(scope || window, k, e);
14450                     }
14451                 }
14452             }
14453         };
14454         this.bindings.push(handler);  
14455         },
14456
14457     /**
14458      * Shorthand for adding a single key listener
14459      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14460      * following options:
14461      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14462      * @param {Function} fn The function to call
14463      * @param {Object} scope (optional) The scope of the function
14464      */
14465     on : function(key, fn, scope){
14466         var keyCode, shift, ctrl, alt;
14467         if(typeof key == "object" && !(key instanceof Array)){
14468             keyCode = key.key;
14469             shift = key.shift;
14470             ctrl = key.ctrl;
14471             alt = key.alt;
14472         }else{
14473             keyCode = key;
14474         }
14475         this.addBinding({
14476             key: keyCode,
14477             shift: shift,
14478             ctrl: ctrl,
14479             alt: alt,
14480             fn: fn,
14481             scope: scope
14482         })
14483     },
14484
14485     // private
14486     handleKeyDown : function(e){
14487             if(this.enabled){ //just in case
14488             var b = this.bindings;
14489             for(var i = 0, len = b.length; i < len; i++){
14490                 b[i].call(this, e);
14491             }
14492             }
14493         },
14494         
14495         /**
14496          * Returns true if this KeyMap is enabled
14497          * @return {Boolean} 
14498          */
14499         isEnabled : function(){
14500             return this.enabled;  
14501         },
14502         
14503         /**
14504          * Enables this KeyMap
14505          */
14506         enable: function(){
14507                 if(!this.enabled){
14508                     this.el.on(this.eventName, this.handleKeyDown, this);
14509                     this.enabled = true;
14510                 }
14511         },
14512
14513         /**
14514          * Disable this KeyMap
14515          */
14516         disable: function(){
14517                 if(this.enabled){
14518                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14519                     this.enabled = false;
14520                 }
14521         }
14522 };/*
14523  * Based on:
14524  * Ext JS Library 1.1.1
14525  * Copyright(c) 2006-2007, Ext JS, LLC.
14526  *
14527  * Originally Released Under LGPL - original licence link has changed is not relivant.
14528  *
14529  * Fork - LGPL
14530  * <script type="text/javascript">
14531  */
14532
14533  
14534 /**
14535  * @class Roo.util.TextMetrics
14536  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14537  * wide, in pixels, a given block of text will be.
14538  * @singleton
14539  */
14540 Roo.util.TextMetrics = function(){
14541     var shared;
14542     return {
14543         /**
14544          * Measures the size of the specified text
14545          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14546          * that can affect the size of the rendered text
14547          * @param {String} text The text to measure
14548          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14549          * in order to accurately measure the text height
14550          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14551          */
14552         measure : function(el, text, fixedWidth){
14553             if(!shared){
14554                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14555             }
14556             shared.bind(el);
14557             shared.setFixedWidth(fixedWidth || 'auto');
14558             return shared.getSize(text);
14559         },
14560
14561         /**
14562          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14563          * the overhead of multiple calls to initialize the style properties on each measurement.
14564          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14565          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14566          * in order to accurately measure the text height
14567          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14568          */
14569         createInstance : function(el, fixedWidth){
14570             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14571         }
14572     };
14573 }();
14574
14575  
14576
14577 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14578     var ml = new Roo.Element(document.createElement('div'));
14579     document.body.appendChild(ml.dom);
14580     ml.position('absolute');
14581     ml.setLeftTop(-1000, -1000);
14582     ml.hide();
14583
14584     if(fixedWidth){
14585         ml.setWidth(fixedWidth);
14586     }
14587      
14588     var instance = {
14589         /**
14590          * Returns the size of the specified text based on the internal element's style and width properties
14591          * @memberOf Roo.util.TextMetrics.Instance#
14592          * @param {String} text The text to measure
14593          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14594          */
14595         getSize : function(text){
14596             ml.update(text);
14597             var s = ml.getSize();
14598             ml.update('');
14599             return s;
14600         },
14601
14602         /**
14603          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14604          * that can affect the size of the rendered text
14605          * @memberOf Roo.util.TextMetrics.Instance#
14606          * @param {String/HTMLElement} el The element, dom node or id
14607          */
14608         bind : function(el){
14609             ml.setStyle(
14610                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14611             );
14612         },
14613
14614         /**
14615          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14616          * to set a fixed width in order to accurately measure the text height.
14617          * @memberOf Roo.util.TextMetrics.Instance#
14618          * @param {Number} width The width to set on the element
14619          */
14620         setFixedWidth : function(width){
14621             ml.setWidth(width);
14622         },
14623
14624         /**
14625          * Returns the measured width of the specified text
14626          * @memberOf Roo.util.TextMetrics.Instance#
14627          * @param {String} text The text to measure
14628          * @return {Number} width The width in pixels
14629          */
14630         getWidth : function(text){
14631             ml.dom.style.width = 'auto';
14632             return this.getSize(text).width;
14633         },
14634
14635         /**
14636          * Returns the measured height of the specified text.  For multiline text, be sure to call
14637          * {@link #setFixedWidth} if necessary.
14638          * @memberOf Roo.util.TextMetrics.Instance#
14639          * @param {String} text The text to measure
14640          * @return {Number} height The height in pixels
14641          */
14642         getHeight : function(text){
14643             return this.getSize(text).height;
14644         }
14645     };
14646
14647     instance.bind(bindTo);
14648
14649     return instance;
14650 };
14651
14652 // backwards compat
14653 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14654  * Based on:
14655  * Ext JS Library 1.1.1
14656  * Copyright(c) 2006-2007, Ext JS, LLC.
14657  *
14658  * Originally Released Under LGPL - original licence link has changed is not relivant.
14659  *
14660  * Fork - LGPL
14661  * <script type="text/javascript">
14662  */
14663
14664 /**
14665  * @class Roo.state.Provider
14666  * Abstract base class for state provider implementations. This class provides methods
14667  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14668  * Provider interface.
14669  */
14670 Roo.state.Provider = function(){
14671     /**
14672      * @event statechange
14673      * Fires when a state change occurs.
14674      * @param {Provider} this This state provider
14675      * @param {String} key The state key which was changed
14676      * @param {String} value The encoded value for the state
14677      */
14678     this.addEvents({
14679         "statechange": true
14680     });
14681     this.state = {};
14682     Roo.state.Provider.superclass.constructor.call(this);
14683 };
14684 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14685     /**
14686      * Returns the current value for a key
14687      * @param {String} name The key name
14688      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14689      * @return {Mixed} The state data
14690      */
14691     get : function(name, defaultValue){
14692         return typeof this.state[name] == "undefined" ?
14693             defaultValue : this.state[name];
14694     },
14695     
14696     /**
14697      * Clears a value from the state
14698      * @param {String} name The key name
14699      */
14700     clear : function(name){
14701         delete this.state[name];
14702         this.fireEvent("statechange", this, name, null);
14703     },
14704     
14705     /**
14706      * Sets the value for a key
14707      * @param {String} name The key name
14708      * @param {Mixed} value The value to set
14709      */
14710     set : function(name, value){
14711         this.state[name] = value;
14712         this.fireEvent("statechange", this, name, value);
14713     },
14714     
14715     /**
14716      * Decodes a string previously encoded with {@link #encodeValue}.
14717      * @param {String} value The value to decode
14718      * @return {Mixed} The decoded value
14719      */
14720     decodeValue : function(cookie){
14721         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14722         var matches = re.exec(unescape(cookie));
14723         if(!matches || !matches[1]) return; // non state cookie
14724         var type = matches[1];
14725         var v = matches[2];
14726         switch(type){
14727             case "n":
14728                 return parseFloat(v);
14729             case "d":
14730                 return new Date(Date.parse(v));
14731             case "b":
14732                 return (v == "1");
14733             case "a":
14734                 var all = [];
14735                 var values = v.split("^");
14736                 for(var i = 0, len = values.length; i < len; i++){
14737                     all.push(this.decodeValue(values[i]));
14738                 }
14739                 return all;
14740            case "o":
14741                 var all = {};
14742                 var values = v.split("^");
14743                 for(var i = 0, len = values.length; i < len; i++){
14744                     var kv = values[i].split("=");
14745                     all[kv[0]] = this.decodeValue(kv[1]);
14746                 }
14747                 return all;
14748            default:
14749                 return v;
14750         }
14751     },
14752     
14753     /**
14754      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14755      * @param {Mixed} value The value to encode
14756      * @return {String} The encoded value
14757      */
14758     encodeValue : function(v){
14759         var enc;
14760         if(typeof v == "number"){
14761             enc = "n:" + v;
14762         }else if(typeof v == "boolean"){
14763             enc = "b:" + (v ? "1" : "0");
14764         }else if(v instanceof Date){
14765             enc = "d:" + v.toGMTString();
14766         }else if(v instanceof Array){
14767             var flat = "";
14768             for(var i = 0, len = v.length; i < len; i++){
14769                 flat += this.encodeValue(v[i]);
14770                 if(i != len-1) flat += "^";
14771             }
14772             enc = "a:" + flat;
14773         }else if(typeof v == "object"){
14774             var flat = "";
14775             for(var key in v){
14776                 if(typeof v[key] != "function"){
14777                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14778                 }
14779             }
14780             enc = "o:" + flat.substring(0, flat.length-1);
14781         }else{
14782             enc = "s:" + v;
14783         }
14784         return escape(enc);        
14785     }
14786 });
14787
14788 /*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798 /**
14799  * @class Roo.state.Manager
14800  * This is the global state manager. By default all components that are "state aware" check this class
14801  * for state information if you don't pass them a custom state provider. In order for this class
14802  * to be useful, it must be initialized with a provider when your application initializes.
14803  <pre><code>
14804 // in your initialization function
14805 init : function(){
14806    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14807    ...
14808    // supposed you have a {@link Roo.BorderLayout}
14809    var layout = new Roo.BorderLayout(...);
14810    layout.restoreState();
14811    // or a {Roo.BasicDialog}
14812    var dialog = new Roo.BasicDialog(...);
14813    dialog.restoreState();
14814  </code></pre>
14815  * @singleton
14816  */
14817 Roo.state.Manager = function(){
14818     var provider = new Roo.state.Provider();
14819     
14820     return {
14821         /**
14822          * Configures the default state provider for your application
14823          * @param {Provider} stateProvider The state provider to set
14824          */
14825         setProvider : function(stateProvider){
14826             provider = stateProvider;
14827         },
14828         
14829         /**
14830          * Returns the current value for a key
14831          * @param {String} name The key name
14832          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14833          * @return {Mixed} The state data
14834          */
14835         get : function(key, defaultValue){
14836             return provider.get(key, defaultValue);
14837         },
14838         
14839         /**
14840          * Sets the value for a key
14841          * @param {String} name The key name
14842          * @param {Mixed} value The state data
14843          */
14844          set : function(key, value){
14845             provider.set(key, value);
14846         },
14847         
14848         /**
14849          * Clears a value from the state
14850          * @param {String} name The key name
14851          */
14852         clear : function(key){
14853             provider.clear(key);
14854         },
14855         
14856         /**
14857          * Gets the currently configured state provider
14858          * @return {Provider} The state provider
14859          */
14860         getProvider : function(){
14861             return provider;
14862         }
14863     };
14864 }();
14865 /*
14866  * Based on:
14867  * Ext JS Library 1.1.1
14868  * Copyright(c) 2006-2007, Ext JS, LLC.
14869  *
14870  * Originally Released Under LGPL - original licence link has changed is not relivant.
14871  *
14872  * Fork - LGPL
14873  * <script type="text/javascript">
14874  */
14875 /**
14876  * @class Roo.state.CookieProvider
14877  * @extends Roo.state.Provider
14878  * The default Provider implementation which saves state via cookies.
14879  * <br />Usage:
14880  <pre><code>
14881    var cp = new Roo.state.CookieProvider({
14882        path: "/cgi-bin/",
14883        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14884        domain: "roojs.com"
14885    })
14886    Roo.state.Manager.setProvider(cp);
14887  </code></pre>
14888  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14889  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14890  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14891  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14892  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14893  * domain the page is running on including the 'www' like 'www.roojs.com')
14894  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14895  * @constructor
14896  * Create a new CookieProvider
14897  * @param {Object} config The configuration object
14898  */
14899 Roo.state.CookieProvider = function(config){
14900     Roo.state.CookieProvider.superclass.constructor.call(this);
14901     this.path = "/";
14902     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14903     this.domain = null;
14904     this.secure = false;
14905     Roo.apply(this, config);
14906     this.state = this.readCookies();
14907 };
14908
14909 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14910     // private
14911     set : function(name, value){
14912         if(typeof value == "undefined" || value === null){
14913             this.clear(name);
14914             return;
14915         }
14916         this.setCookie(name, value);
14917         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14918     },
14919
14920     // private
14921     clear : function(name){
14922         this.clearCookie(name);
14923         Roo.state.CookieProvider.superclass.clear.call(this, name);
14924     },
14925
14926     // private
14927     readCookies : function(){
14928         var cookies = {};
14929         var c = document.cookie + ";";
14930         var re = /\s?(.*?)=(.*?);/g;
14931         var matches;
14932         while((matches = re.exec(c)) != null){
14933             var name = matches[1];
14934             var value = matches[2];
14935             if(name && name.substring(0,3) == "ys-"){
14936                 cookies[name.substr(3)] = this.decodeValue(value);
14937             }
14938         }
14939         return cookies;
14940     },
14941
14942     // private
14943     setCookie : function(name, value){
14944         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14945            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14946            ((this.path == null) ? "" : ("; path=" + this.path)) +
14947            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14948            ((this.secure == true) ? "; secure" : "");
14949     },
14950
14951     // private
14952     clearCookie : function(name){
14953         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14954            ((this.path == null) ? "" : ("; path=" + this.path)) +
14955            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14956            ((this.secure == true) ? "; secure" : "");
14957     }
14958 });/*
14959  * Based on:
14960  * Ext JS Library 1.1.1
14961  * Copyright(c) 2006-2007, Ext JS, LLC.
14962  *
14963  * Originally Released Under LGPL - original licence link has changed is not relivant.
14964  *
14965  * Fork - LGPL
14966  * <script type="text/javascript">
14967  */
14968  
14969
14970 /**
14971  * @class Roo.ComponentMgr
14972  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14973  * @singleton
14974  */
14975 Roo.ComponentMgr = function(){
14976     var all = new Roo.util.MixedCollection();
14977
14978     return {
14979         /**
14980          * Registers a component.
14981          * @param {Roo.Component} c The component
14982          */
14983         register : function(c){
14984             all.add(c);
14985         },
14986
14987         /**
14988          * Unregisters a component.
14989          * @param {Roo.Component} c The component
14990          */
14991         unregister : function(c){
14992             all.remove(c);
14993         },
14994
14995         /**
14996          * Returns a component by id
14997          * @param {String} id The component id
14998          */
14999         get : function(id){
15000             return all.get(id);
15001         },
15002
15003         /**
15004          * Registers a function that will be called when a specified component is added to ComponentMgr
15005          * @param {String} id The component id
15006          * @param {Funtction} fn The callback function
15007          * @param {Object} scope The scope of the callback
15008          */
15009         onAvailable : function(id, fn, scope){
15010             all.on("add", function(index, o){
15011                 if(o.id == id){
15012                     fn.call(scope || o, o);
15013                     all.un("add", fn, scope);
15014                 }
15015             });
15016         }
15017     };
15018 }();/*
15019  * Based on:
15020  * Ext JS Library 1.1.1
15021  * Copyright(c) 2006-2007, Ext JS, LLC.
15022  *
15023  * Originally Released Under LGPL - original licence link has changed is not relivant.
15024  *
15025  * Fork - LGPL
15026  * <script type="text/javascript">
15027  */
15028  
15029 /**
15030  * @class Roo.Component
15031  * @extends Roo.util.Observable
15032  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15033  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15034  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15035  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15036  * All visual components (widgets) that require rendering into a layout should subclass Component.
15037  * @constructor
15038  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15039  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15040  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15041  */
15042 Roo.Component = function(config){
15043     config = config || {};
15044     if(config.tagName || config.dom || typeof config == "string"){ // element object
15045         config = {el: config, id: config.id || config};
15046     }
15047     this.initialConfig = config;
15048
15049     Roo.apply(this, config);
15050     this.addEvents({
15051         /**
15052          * @event disable
15053          * Fires after the component is disabled.
15054              * @param {Roo.Component} this
15055              */
15056         disable : true,
15057         /**
15058          * @event enable
15059          * Fires after the component is enabled.
15060              * @param {Roo.Component} this
15061              */
15062         enable : true,
15063         /**
15064          * @event beforeshow
15065          * Fires before the component is shown.  Return false to stop the show.
15066              * @param {Roo.Component} this
15067              */
15068         beforeshow : true,
15069         /**
15070          * @event show
15071          * Fires after the component is shown.
15072              * @param {Roo.Component} this
15073              */
15074         show : true,
15075         /**
15076          * @event beforehide
15077          * Fires before the component is hidden. Return false to stop the hide.
15078              * @param {Roo.Component} this
15079              */
15080         beforehide : true,
15081         /**
15082          * @event hide
15083          * Fires after the component is hidden.
15084              * @param {Roo.Component} this
15085              */
15086         hide : true,
15087         /**
15088          * @event beforerender
15089          * Fires before the component is rendered. Return false to stop the render.
15090              * @param {Roo.Component} this
15091              */
15092         beforerender : true,
15093         /**
15094          * @event render
15095          * Fires after the component is rendered.
15096              * @param {Roo.Component} this
15097              */
15098         render : true,
15099         /**
15100          * @event beforedestroy
15101          * Fires before the component is destroyed. Return false to stop the destroy.
15102              * @param {Roo.Component} this
15103              */
15104         beforedestroy : true,
15105         /**
15106          * @event destroy
15107          * Fires after the component is destroyed.
15108              * @param {Roo.Component} this
15109              */
15110         destroy : true
15111     });
15112     if(!this.id){
15113         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15114     }
15115     Roo.ComponentMgr.register(this);
15116     Roo.Component.superclass.constructor.call(this);
15117     this.initComponent();
15118     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15119         this.render(this.renderTo);
15120         delete this.renderTo;
15121     }
15122 };
15123
15124 /** @private */
15125 Roo.Component.AUTO_ID = 1000;
15126
15127 Roo.extend(Roo.Component, Roo.util.Observable, {
15128     /**
15129      * @scope Roo.Component.prototype
15130      * @type {Boolean}
15131      * true if this component is hidden. Read-only.
15132      */
15133     hidden : false,
15134     /**
15135      * @type {Boolean}
15136      * true if this component is disabled. Read-only.
15137      */
15138     disabled : false,
15139     /**
15140      * @type {Boolean}
15141      * true if this component has been rendered. Read-only.
15142      */
15143     rendered : false,
15144     
15145     /** @cfg {String} disableClass
15146      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15147      */
15148     disabledClass : "x-item-disabled",
15149         /** @cfg {Boolean} allowDomMove
15150          * Whether the component can move the Dom node when rendering (defaults to true).
15151          */
15152     allowDomMove : true,
15153     /** @cfg {String} hideMode
15154      * How this component should hidden. Supported values are
15155      * "visibility" (css visibility), "offsets" (negative offset position) and
15156      * "display" (css display) - defaults to "display".
15157      */
15158     hideMode: 'display',
15159
15160     /** @private */
15161     ctype : "Roo.Component",
15162
15163     /**
15164      * @cfg {String} actionMode 
15165      * which property holds the element that used for  hide() / show() / disable() / enable()
15166      * default is 'el' 
15167      */
15168     actionMode : "el",
15169
15170     /** @private */
15171     getActionEl : function(){
15172         return this[this.actionMode];
15173     },
15174
15175     initComponent : Roo.emptyFn,
15176     /**
15177      * If this is a lazy rendering component, render it to its container element.
15178      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15179      */
15180     render : function(container, position){
15181         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15182             if(!container && this.el){
15183                 this.el = Roo.get(this.el);
15184                 container = this.el.dom.parentNode;
15185                 this.allowDomMove = false;
15186             }
15187             this.container = Roo.get(container);
15188             this.rendered = true;
15189             if(position !== undefined){
15190                 if(typeof position == 'number'){
15191                     position = this.container.dom.childNodes[position];
15192                 }else{
15193                     position = Roo.getDom(position);
15194                 }
15195             }
15196             this.onRender(this.container, position || null);
15197             if(this.cls){
15198                 this.el.addClass(this.cls);
15199                 delete this.cls;
15200             }
15201             if(this.style){
15202                 this.el.applyStyles(this.style);
15203                 delete this.style;
15204             }
15205             this.fireEvent("render", this);
15206             this.afterRender(this.container);
15207             if(this.hidden){
15208                 this.hide();
15209             }
15210             if(this.disabled){
15211                 this.disable();
15212             }
15213         }
15214         return this;
15215     },
15216
15217     /** @private */
15218     // default function is not really useful
15219     onRender : function(ct, position){
15220         if(this.el){
15221             this.el = Roo.get(this.el);
15222             if(this.allowDomMove !== false){
15223                 ct.dom.insertBefore(this.el.dom, position);
15224             }
15225         }
15226     },
15227
15228     /** @private */
15229     getAutoCreate : function(){
15230         var cfg = typeof this.autoCreate == "object" ?
15231                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15232         if(this.id && !cfg.id){
15233             cfg.id = this.id;
15234         }
15235         return cfg;
15236     },
15237
15238     /** @private */
15239     afterRender : Roo.emptyFn,
15240
15241     /**
15242      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15243      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15244      */
15245     destroy : function(){
15246         if(this.fireEvent("beforedestroy", this) !== false){
15247             this.purgeListeners();
15248             this.beforeDestroy();
15249             if(this.rendered){
15250                 this.el.removeAllListeners();
15251                 this.el.remove();
15252                 if(this.actionMode == "container"){
15253                     this.container.remove();
15254                 }
15255             }
15256             this.onDestroy();
15257             Roo.ComponentMgr.unregister(this);
15258             this.fireEvent("destroy", this);
15259         }
15260     },
15261
15262         /** @private */
15263     beforeDestroy : function(){
15264
15265     },
15266
15267         /** @private */
15268         onDestroy : function(){
15269
15270     },
15271
15272     /**
15273      * Returns the underlying {@link Roo.Element}.
15274      * @return {Roo.Element} The element
15275      */
15276     getEl : function(){
15277         return this.el;
15278     },
15279
15280     /**
15281      * Returns the id of this component.
15282      * @return {String}
15283      */
15284     getId : function(){
15285         return this.id;
15286     },
15287
15288     /**
15289      * Try to focus this component.
15290      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15291      * @return {Roo.Component} this
15292      */
15293     focus : function(selectText){
15294         if(this.rendered){
15295             this.el.focus();
15296             if(selectText === true){
15297                 this.el.dom.select();
15298             }
15299         }
15300         return this;
15301     },
15302
15303     /** @private */
15304     blur : function(){
15305         if(this.rendered){
15306             this.el.blur();
15307         }
15308         return this;
15309     },
15310
15311     /**
15312      * Disable this component.
15313      * @return {Roo.Component} this
15314      */
15315     disable : function(){
15316         if(this.rendered){
15317             this.onDisable();
15318         }
15319         this.disabled = true;
15320         this.fireEvent("disable", this);
15321         return this;
15322     },
15323
15324         // private
15325     onDisable : function(){
15326         this.getActionEl().addClass(this.disabledClass);
15327         this.el.dom.disabled = true;
15328     },
15329
15330     /**
15331      * Enable this component.
15332      * @return {Roo.Component} this
15333      */
15334     enable : function(){
15335         if(this.rendered){
15336             this.onEnable();
15337         }
15338         this.disabled = false;
15339         this.fireEvent("enable", this);
15340         return this;
15341     },
15342
15343         // private
15344     onEnable : function(){
15345         this.getActionEl().removeClass(this.disabledClass);
15346         this.el.dom.disabled = false;
15347     },
15348
15349     /**
15350      * Convenience function for setting disabled/enabled by boolean.
15351      * @param {Boolean} disabled
15352      */
15353     setDisabled : function(disabled){
15354         this[disabled ? "disable" : "enable"]();
15355     },
15356
15357     /**
15358      * Show this component.
15359      * @return {Roo.Component} this
15360      */
15361     show: function(){
15362         if(this.fireEvent("beforeshow", this) !== false){
15363             this.hidden = false;
15364             if(this.rendered){
15365                 this.onShow();
15366             }
15367             this.fireEvent("show", this);
15368         }
15369         return this;
15370     },
15371
15372     // private
15373     onShow : function(){
15374         var ae = this.getActionEl();
15375         if(this.hideMode == 'visibility'){
15376             ae.dom.style.visibility = "visible";
15377         }else if(this.hideMode == 'offsets'){
15378             ae.removeClass('x-hidden');
15379         }else{
15380             ae.dom.style.display = "";
15381         }
15382     },
15383
15384     /**
15385      * Hide this component.
15386      * @return {Roo.Component} this
15387      */
15388     hide: function(){
15389         if(this.fireEvent("beforehide", this) !== false){
15390             this.hidden = true;
15391             if(this.rendered){
15392                 this.onHide();
15393             }
15394             this.fireEvent("hide", this);
15395         }
15396         return this;
15397     },
15398
15399     // private
15400     onHide : function(){
15401         var ae = this.getActionEl();
15402         if(this.hideMode == 'visibility'){
15403             ae.dom.style.visibility = "hidden";
15404         }else if(this.hideMode == 'offsets'){
15405             ae.addClass('x-hidden');
15406         }else{
15407             ae.dom.style.display = "none";
15408         }
15409     },
15410
15411     /**
15412      * Convenience function to hide or show this component by boolean.
15413      * @param {Boolean} visible True to show, false to hide
15414      * @return {Roo.Component} this
15415      */
15416     setVisible: function(visible){
15417         if(visible) {
15418             this.show();
15419         }else{
15420             this.hide();
15421         }
15422         return this;
15423     },
15424
15425     /**
15426      * Returns true if this component is visible.
15427      */
15428     isVisible : function(){
15429         return this.getActionEl().isVisible();
15430     },
15431
15432     cloneConfig : function(overrides){
15433         overrides = overrides || {};
15434         var id = overrides.id || Roo.id();
15435         var cfg = Roo.applyIf(overrides, this.initialConfig);
15436         cfg.id = id; // prevent dup id
15437         return new this.constructor(cfg);
15438     }
15439 });/*
15440  * Based on:
15441  * Ext JS Library 1.1.1
15442  * Copyright(c) 2006-2007, Ext JS, LLC.
15443  *
15444  * Originally Released Under LGPL - original licence link has changed is not relivant.
15445  *
15446  * Fork - LGPL
15447  * <script type="text/javascript">
15448  */
15449
15450 /**
15451  * @class Roo.BoxComponent
15452  * @extends Roo.Component
15453  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15454  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15455  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15456  * layout containers.
15457  * @constructor
15458  * @param {Roo.Element/String/Object} config The configuration options.
15459  */
15460 Roo.BoxComponent = function(config){
15461     Roo.Component.call(this, config);
15462     this.addEvents({
15463         /**
15464          * @event resize
15465          * Fires after the component is resized.
15466              * @param {Roo.Component} this
15467              * @param {Number} adjWidth The box-adjusted width that was set
15468              * @param {Number} adjHeight The box-adjusted height that was set
15469              * @param {Number} rawWidth The width that was originally specified
15470              * @param {Number} rawHeight The height that was originally specified
15471              */
15472         resize : true,
15473         /**
15474          * @event move
15475          * Fires after the component is moved.
15476              * @param {Roo.Component} this
15477              * @param {Number} x The new x position
15478              * @param {Number} y The new y position
15479              */
15480         move : true
15481     });
15482 };
15483
15484 Roo.extend(Roo.BoxComponent, Roo.Component, {
15485     // private, set in afterRender to signify that the component has been rendered
15486     boxReady : false,
15487     // private, used to defer height settings to subclasses
15488     deferHeight: false,
15489     /** @cfg {Number} width
15490      * width (optional) size of component
15491      */
15492      /** @cfg {Number} height
15493      * height (optional) size of component
15494      */
15495      
15496     /**
15497      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15498      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15499      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15500      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15501      * @return {Roo.BoxComponent} this
15502      */
15503     setSize : function(w, h){
15504         // support for standard size objects
15505         if(typeof w == 'object'){
15506             h = w.height;
15507             w = w.width;
15508         }
15509         // not rendered
15510         if(!this.boxReady){
15511             this.width = w;
15512             this.height = h;
15513             return this;
15514         }
15515
15516         // prevent recalcs when not needed
15517         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15518             return this;
15519         }
15520         this.lastSize = {width: w, height: h};
15521
15522         var adj = this.adjustSize(w, h);
15523         var aw = adj.width, ah = adj.height;
15524         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15525             var rz = this.getResizeEl();
15526             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15527                 rz.setSize(aw, ah);
15528             }else if(!this.deferHeight && ah !== undefined){
15529                 rz.setHeight(ah);
15530             }else if(aw !== undefined){
15531                 rz.setWidth(aw);
15532             }
15533             this.onResize(aw, ah, w, h);
15534             this.fireEvent('resize', this, aw, ah, w, h);
15535         }
15536         return this;
15537     },
15538
15539     /**
15540      * Gets the current size of the component's underlying element.
15541      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15542      */
15543     getSize : function(){
15544         return this.el.getSize();
15545     },
15546
15547     /**
15548      * Gets the current XY position of the component's underlying element.
15549      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15550      * @return {Array} The XY position of the element (e.g., [100, 200])
15551      */
15552     getPosition : function(local){
15553         if(local === true){
15554             return [this.el.getLeft(true), this.el.getTop(true)];
15555         }
15556         return this.xy || this.el.getXY();
15557     },
15558
15559     /**
15560      * Gets the current box measurements of the component's underlying element.
15561      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15562      * @returns {Object} box An object in the format {x, y, width, height}
15563      */
15564     getBox : function(local){
15565         var s = this.el.getSize();
15566         if(local){
15567             s.x = this.el.getLeft(true);
15568             s.y = this.el.getTop(true);
15569         }else{
15570             var xy = this.xy || this.el.getXY();
15571             s.x = xy[0];
15572             s.y = xy[1];
15573         }
15574         return s;
15575     },
15576
15577     /**
15578      * Sets the current box measurements of the component's underlying element.
15579      * @param {Object} box An object in the format {x, y, width, height}
15580      * @returns {Roo.BoxComponent} this
15581      */
15582     updateBox : function(box){
15583         this.setSize(box.width, box.height);
15584         this.setPagePosition(box.x, box.y);
15585         return this;
15586     },
15587
15588     // protected
15589     getResizeEl : function(){
15590         return this.resizeEl || this.el;
15591     },
15592
15593     // protected
15594     getPositionEl : function(){
15595         return this.positionEl || this.el;
15596     },
15597
15598     /**
15599      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15600      * This method fires the move event.
15601      * @param {Number} left The new left
15602      * @param {Number} top The new top
15603      * @returns {Roo.BoxComponent} this
15604      */
15605     setPosition : function(x, y){
15606         this.x = x;
15607         this.y = y;
15608         if(!this.boxReady){
15609             return this;
15610         }
15611         var adj = this.adjustPosition(x, y);
15612         var ax = adj.x, ay = adj.y;
15613
15614         var el = this.getPositionEl();
15615         if(ax !== undefined || ay !== undefined){
15616             if(ax !== undefined && ay !== undefined){
15617                 el.setLeftTop(ax, ay);
15618             }else if(ax !== undefined){
15619                 el.setLeft(ax);
15620             }else if(ay !== undefined){
15621                 el.setTop(ay);
15622             }
15623             this.onPosition(ax, ay);
15624             this.fireEvent('move', this, ax, ay);
15625         }
15626         return this;
15627     },
15628
15629     /**
15630      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15631      * This method fires the move event.
15632      * @param {Number} x The new x position
15633      * @param {Number} y The new y position
15634      * @returns {Roo.BoxComponent} this
15635      */
15636     setPagePosition : function(x, y){
15637         this.pageX = x;
15638         this.pageY = y;
15639         if(!this.boxReady){
15640             return;
15641         }
15642         if(x === undefined || y === undefined){ // cannot translate undefined points
15643             return;
15644         }
15645         var p = this.el.translatePoints(x, y);
15646         this.setPosition(p.left, p.top);
15647         return this;
15648     },
15649
15650     // private
15651     onRender : function(ct, position){
15652         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15653         if(this.resizeEl){
15654             this.resizeEl = Roo.get(this.resizeEl);
15655         }
15656         if(this.positionEl){
15657             this.positionEl = Roo.get(this.positionEl);
15658         }
15659     },
15660
15661     // private
15662     afterRender : function(){
15663         Roo.BoxComponent.superclass.afterRender.call(this);
15664         this.boxReady = true;
15665         this.setSize(this.width, this.height);
15666         if(this.x || this.y){
15667             this.setPosition(this.x, this.y);
15668         }
15669         if(this.pageX || this.pageY){
15670             this.setPagePosition(this.pageX, this.pageY);
15671         }
15672     },
15673
15674     /**
15675      * Force the component's size to recalculate based on the underlying element's current height and width.
15676      * @returns {Roo.BoxComponent} this
15677      */
15678     syncSize : function(){
15679         delete this.lastSize;
15680         this.setSize(this.el.getWidth(), this.el.getHeight());
15681         return this;
15682     },
15683
15684     /**
15685      * Called after the component is resized, this method is empty by default but can be implemented by any
15686      * subclass that needs to perform custom logic after a resize occurs.
15687      * @param {Number} adjWidth The box-adjusted width that was set
15688      * @param {Number} adjHeight The box-adjusted height that was set
15689      * @param {Number} rawWidth The width that was originally specified
15690      * @param {Number} rawHeight The height that was originally specified
15691      */
15692     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15693
15694     },
15695
15696     /**
15697      * Called after the component is moved, this method is empty by default but can be implemented by any
15698      * subclass that needs to perform custom logic after a move occurs.
15699      * @param {Number} x The new x position
15700      * @param {Number} y The new y position
15701      */
15702     onPosition : function(x, y){
15703
15704     },
15705
15706     // private
15707     adjustSize : function(w, h){
15708         if(this.autoWidth){
15709             w = 'auto';
15710         }
15711         if(this.autoHeight){
15712             h = 'auto';
15713         }
15714         return {width : w, height: h};
15715     },
15716
15717     // private
15718     adjustPosition : function(x, y){
15719         return {x : x, y: y};
15720     }
15721 });/*
15722  * Original code for Roojs - LGPL
15723  * <script type="text/javascript">
15724  */
15725  
15726 /**
15727  * @class Roo.XComponent
15728  * A delayed Element creator...
15729  * Or a way to group chunks of interface together.
15730  * 
15731  * Mypart.xyx = new Roo.XComponent({
15732
15733     parent : 'Mypart.xyz', // empty == document.element.!!
15734     order : '001',
15735     name : 'xxxx'
15736     region : 'xxxx'
15737     disabled : function() {} 
15738      
15739     tree : function() { // return an tree of xtype declared components
15740         var MODULE = this;
15741         return 
15742         {
15743             xtype : 'NestedLayoutPanel',
15744             // technicall
15745         }
15746      ]
15747  *})
15748  *
15749  *
15750  * It can be used to build a big heiracy, with parent etc.
15751  * or you can just use this to render a single compoent to a dom element
15752  * MYPART.render(Roo.Element | String(id) | dom_element )
15753  * 
15754  * @extends Roo.util.Observable
15755  * @constructor
15756  * @param cfg {Object} configuration of component
15757  * 
15758  */
15759 Roo.XComponent = function(cfg) {
15760     Roo.apply(this, cfg);
15761     this.addEvents({ 
15762         /**
15763              * @event built
15764              * Fires when this the componnt is built
15765              * @param {Roo.XComponent} c the component
15766              */
15767         'built' : true
15768         
15769     });
15770     this.region = this.region || 'center'; // default..
15771     Roo.XComponent.register(this);
15772     this.modules = false;
15773     this.el = false; // where the layout goes..
15774     
15775     
15776 }
15777 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15778     /**
15779      * @property el
15780      * The created element (with Roo.factory())
15781      * @type {Roo.Layout}
15782      */
15783     el  : false,
15784     
15785     /**
15786      * @property el
15787      * for BC  - use el in new code
15788      * @type {Roo.Layout}
15789      */
15790     panel : false,
15791     
15792     /**
15793      * @property layout
15794      * for BC  - use el in new code
15795      * @type {Roo.Layout}
15796      */
15797     layout : false,
15798     
15799      /**
15800      * @cfg {Function|boolean} disabled
15801      * If this module is disabled by some rule, return true from the funtion
15802      */
15803     disabled : false,
15804     
15805     /**
15806      * @cfg {String} parent 
15807      * Name of parent element which it get xtype added to..
15808      */
15809     parent: false,
15810     
15811     /**
15812      * @cfg {String} order
15813      * Used to set the order in which elements are created (usefull for multiple tabs)
15814      */
15815     
15816     order : false,
15817     /**
15818      * @cfg {String} name
15819      * String to display while loading.
15820      */
15821     name : false,
15822     /**
15823      * @cfg {String} region
15824      * Region to render component to (defaults to center)
15825      */
15826     region : 'center',
15827     
15828     /**
15829      * @cfg {Array} items
15830      * A single item array - the first element is the root of the tree..
15831      * It's done this way to stay compatible with the Xtype system...
15832      */
15833     items : false,
15834     
15835     /**
15836      * @property _tree
15837      * The method that retuns the tree of parts that make up this compoennt 
15838      * @type {function}
15839      */
15840     _tree  : false,
15841     
15842      /**
15843      * render
15844      * render element to dom or tree
15845      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15846      */
15847     
15848     render : function(el)
15849     {
15850         
15851         el = el || false;
15852         var hp = this.parent ? 1 : 0;
15853         
15854         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15855             // if parent is a '#.....' string, then let's use that..
15856             var ename = this.parent.substr(1)
15857             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15858             el = Roo.get(ename);
15859             if (!el && !this.parent) {
15860                 Roo.log("Warning - element can not be found :#" + ename );
15861                 return;
15862             }
15863         }
15864         
15865         
15866         if (!this.parent) {
15867             
15868             el = el ? Roo.get(el) : false;      
15869             
15870             // it's a top level one..
15871             this.parent =  {
15872                 el : new Roo.BorderLayout(el || document.body, {
15873                 
15874                      center: {
15875                          titlebar: false,
15876                          autoScroll:false,
15877                          closeOnTab: true,
15878                          tabPosition: 'top',
15879                           //resizeTabs: true,
15880                          alwaysShowTabs: el && hp? false :  true,
15881                          hideTabs: el || !hp ? true :  false,
15882                          minTabWidth: 140
15883                      }
15884                  })
15885             }
15886         }
15887         
15888                 if (!this.parent.el) {
15889                         // probably an old style ctor, which has been disabled.
15890                         return;
15891                         
15892                 }
15893                 // The 'tree' method is  '_tree now' 
15894             
15895         var tree = this._tree ? this._tree() : this.tree();
15896         tree.region = tree.region || this.region;
15897         this.el = this.parent.el.addxtype(tree);
15898         this.fireEvent('built', this);
15899         
15900         this.panel = this.el;
15901         this.layout = this.panel.layout;
15902                 this.parentLayout = this.parent.layout  || false;  
15903          
15904     }
15905     
15906 });
15907
15908 Roo.apply(Roo.XComponent, {
15909     /**
15910      * @property  hideProgress
15911      * true to disable the building progress bar.. usefull on single page renders.
15912      * @type Boolean
15913      */
15914     hideProgress : false,
15915     /**
15916      * @property  buildCompleted
15917      * True when the builder has completed building the interface.
15918      * @type Boolean
15919      */
15920     buildCompleted : false,
15921      
15922     /**
15923      * @property  topModule
15924      * the upper most module - uses document.element as it's constructor.
15925      * @type Object
15926      */
15927      
15928     topModule  : false,
15929       
15930     /**
15931      * @property  modules
15932      * array of modules to be created by registration system.
15933      * @type {Array} of Roo.XComponent
15934      */
15935     
15936     modules : [],
15937     /**
15938      * @property  elmodules
15939      * array of modules to be created by which use #ID 
15940      * @type {Array} of Roo.XComponent
15941      */
15942      
15943     elmodules : [],
15944
15945     
15946     /**
15947      * Register components to be built later.
15948      *
15949      * This solves the following issues
15950      * - Building is not done on page load, but after an authentication process has occured.
15951      * - Interface elements are registered on page load
15952      * - Parent Interface elements may not be loaded before child, so this handles that..
15953      * 
15954      *
15955      * example:
15956      * 
15957      * MyApp.register({
15958           order : '000001',
15959           module : 'Pman.Tab.projectMgr',
15960           region : 'center',
15961           parent : 'Pman.layout',
15962           disabled : false,  // or use a function..
15963         })
15964      
15965      * * @param {Object} details about module
15966      */
15967     register : function(obj) {
15968                 
15969         Roo.XComponent.event.fireEvent('register', obj);
15970         switch(typeof(obj.disabled) ) {
15971                 
15972             case 'undefined':
15973                 break;
15974             
15975             case 'function':
15976                 if ( obj.disabled() ) {
15977                         return;
15978                 }
15979                 break;
15980             
15981             default:
15982                 if (obj.disabled) {
15983                         return;
15984                 }
15985                 break;
15986         }
15987                 
15988         this.modules.push(obj);
15989          
15990     },
15991     /**
15992      * convert a string to an object..
15993      * eg. 'AAA.BBB' -> finds AAA.BBB
15994
15995      */
15996     
15997     toObject : function(str)
15998     {
15999         if (!str || typeof(str) == 'object') {
16000             return str;
16001         }
16002         if (str.substring(0,1) == '#') {
16003             return str;
16004         }
16005
16006         var ar = str.split('.');
16007         var rt, o;
16008         rt = ar.shift();
16009             /** eval:var:o */
16010         try {
16011             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16012         } catch (e) {
16013             throw "Module not found : " + str;
16014         }
16015         
16016         if (o === false) {
16017             throw "Module not found : " + str;
16018         }
16019         Roo.each(ar, function(e) {
16020             if (typeof(o[e]) == 'undefined') {
16021                 throw "Module not found : " + str;
16022             }
16023             o = o[e];
16024         });
16025         
16026         return o;
16027         
16028     },
16029     
16030     
16031     /**
16032      * move modules into their correct place in the tree..
16033      * 
16034      */
16035     preBuild : function ()
16036     {
16037         var _t = this;
16038         Roo.each(this.modules , function (obj)
16039         {
16040             Roo.XComponent.event.fireEvent('beforebuild', obj);
16041             
16042             var opar = obj.parent;
16043             try { 
16044                 obj.parent = this.toObject(opar);
16045             } catch(e) {
16046                 Roo.log("parent:toObject failed: " + e.toString());
16047                 return;
16048             }
16049             
16050             if (!obj.parent) {
16051                 Roo.debug && Roo.log("GOT top level module");
16052                 Roo.debug && Roo.log(obj);
16053                 obj.modules = new Roo.util.MixedCollection(false, 
16054                     function(o) { return o.order + '' }
16055                 );
16056                 this.topModule = obj;
16057                 return;
16058             }
16059                         // parent is a string (usually a dom element name..)
16060             if (typeof(obj.parent) == 'string') {
16061                 this.elmodules.push(obj);
16062                 return;
16063             }
16064             if (obj.parent.constructor != Roo.XComponent) {
16065                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16066             }
16067             if (!obj.parent.modules) {
16068                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16069                     function(o) { return o.order + '' }
16070                 );
16071             }
16072             if (obj.parent.disabled) {
16073                 obj.disabled = true;
16074             }
16075             obj.parent.modules.add(obj);
16076         }, this);
16077     },
16078     
16079      /**
16080      * make a list of modules to build.
16081      * @return {Array} list of modules. 
16082      */ 
16083     
16084     buildOrder : function()
16085     {
16086         var _this = this;
16087         var cmp = function(a,b) {   
16088             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16089         };
16090         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16091             throw "No top level modules to build";
16092         }
16093         
16094         // make a flat list in order of modules to build.
16095         var mods = this.topModule ? [ this.topModule ] : [];
16096                 
16097         
16098         // elmodules (is a list of DOM based modules )
16099         Roo.each(this.elmodules, function(e) {
16100             mods.push(e);
16101             if (!this.topModule &&
16102                 typeof(e.parent) == 'string' &&
16103                 e.parent.substring(0,1) == '#' &&
16104                 Roo.get(e.parent.substr(1))
16105                ) {
16106                 
16107                 _this.topModule = e;
16108             }
16109             
16110         });
16111
16112         
16113         // add modules to their parents..
16114         var addMod = function(m) {
16115             Roo.debug && Roo.log("build Order: add: " + m.name);
16116                 
16117             mods.push(m);
16118             if (m.modules && !m.disabled) {
16119                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16120                 m.modules.keySort('ASC',  cmp );
16121                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16122     
16123                 m.modules.each(addMod);
16124             } else {
16125                 Roo.debug && Roo.log("build Order: no child modules");
16126             }
16127             // not sure if this is used any more..
16128             if (m.finalize) {
16129                 m.finalize.name = m.name + " (clean up) ";
16130                 mods.push(m.finalize);
16131             }
16132             
16133         }
16134         if (this.topModule && this.topModule.modules) { 
16135             this.topModule.modules.keySort('ASC',  cmp );
16136             this.topModule.modules.each(addMod);
16137         } 
16138         return mods;
16139     },
16140     
16141      /**
16142      * Build the registered modules.
16143      * @param {Object} parent element.
16144      * @param {Function} optional method to call after module has been added.
16145      * 
16146      */ 
16147    
16148     build : function() 
16149     {
16150         
16151         this.preBuild();
16152         var mods = this.buildOrder();
16153       
16154         //this.allmods = mods;
16155         //Roo.debug && Roo.log(mods);
16156         //return;
16157         if (!mods.length) { // should not happen
16158             throw "NO modules!!!";
16159         }
16160         
16161         
16162         var msg = "Building Interface...";
16163         // flash it up as modal - so we store the mask!?
16164         if (!this.hideProgress) {
16165             Roo.MessageBox.show({ title: 'loading' });
16166             Roo.MessageBox.show({
16167                title: "Please wait...",
16168                msg: msg,
16169                width:450,
16170                progress:true,
16171                closable:false,
16172                modal: false
16173               
16174             });
16175         }
16176         var total = mods.length;
16177         
16178         var _this = this;
16179         var progressRun = function() {
16180             if (!mods.length) {
16181                 Roo.debug && Roo.log('hide?');
16182                 if (!this.hideProgress) {
16183                     Roo.MessageBox.hide();
16184                 }
16185                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16186                 
16187                 // THE END...
16188                 return false;   
16189             }
16190             
16191             var m = mods.shift();
16192             
16193             
16194             Roo.debug && Roo.log(m);
16195             // not sure if this is supported any more.. - modules that are are just function
16196             if (typeof(m) == 'function') { 
16197                 m.call(this);
16198                 return progressRun.defer(10, _this);
16199             } 
16200             
16201             
16202             msg = "Building Interface " + (total  - mods.length) + 
16203                     " of " + total + 
16204                     (m.name ? (' - ' + m.name) : '');
16205                         Roo.debug && Roo.log(msg);
16206             if (!this.hideProgress) { 
16207                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16208             }
16209             
16210          
16211             // is the module disabled?
16212             var disabled = (typeof(m.disabled) == 'function') ?
16213                 m.disabled.call(m.module.disabled) : m.disabled;    
16214             
16215             
16216             if (disabled) {
16217                 return progressRun(); // we do not update the display!
16218             }
16219             
16220             // now build 
16221             
16222                         
16223                         
16224             m.render();
16225             // it's 10 on top level, and 1 on others??? why...
16226             return progressRun.defer(10, _this);
16227              
16228         }
16229         progressRun.defer(1, _this);
16230      
16231         
16232         
16233     },
16234         
16235         
16236         /**
16237          * Event Object.
16238          *
16239          *
16240          */
16241         event: false, 
16242     /**
16243          * wrapper for event.on - aliased later..  
16244          * Typically use to register a event handler for register:
16245          *
16246          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16247          *
16248          */
16249     on : false
16250    
16251     
16252     
16253 });
16254
16255 Roo.XComponent.event = new Roo.util.Observable({
16256                 events : { 
16257                         /**
16258                          * @event register
16259                          * Fires when an Component is registered,
16260                          * set the disable property on the Component to stop registration.
16261                          * @param {Roo.XComponent} c the component being registerd.
16262                          * 
16263                          */
16264                         'register' : true,
16265             /**
16266                          * @event beforebuild
16267                          * Fires before each Component is built
16268                          * can be used to apply permissions.
16269                          * @param {Roo.XComponent} c the component being registerd.
16270                          * 
16271                          */
16272                         'beforebuild' : true,
16273                         /**
16274                          * @event buildcomplete
16275                          * Fires on the top level element when all elements have been built
16276                          * @param {Roo.XComponent} the top level component.
16277                          */
16278                         'buildcomplete' : true
16279                         
16280                 }
16281 });
16282
16283 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16284  /*
16285  * Based on:
16286  * Ext JS Library 1.1.1
16287  * Copyright(c) 2006-2007, Ext JS, LLC.
16288  *
16289  * Originally Released Under LGPL - original licence link has changed is not relivant.
16290  *
16291  * Fork - LGPL
16292  * <script type="text/javascript">
16293  */
16294
16295
16296
16297 /*
16298  * These classes are derivatives of the similarly named classes in the YUI Library.
16299  * The original license:
16300  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16301  * Code licensed under the BSD License:
16302  * http://developer.yahoo.net/yui/license.txt
16303  */
16304
16305 (function() {
16306
16307 var Event=Roo.EventManager;
16308 var Dom=Roo.lib.Dom;
16309
16310 /**
16311  * @class Roo.dd.DragDrop
16312  * @extends Roo.util.Observable
16313  * Defines the interface and base operation of items that that can be
16314  * dragged or can be drop targets.  It was designed to be extended, overriding
16315  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16316  * Up to three html elements can be associated with a DragDrop instance:
16317  * <ul>
16318  * <li>linked element: the element that is passed into the constructor.
16319  * This is the element which defines the boundaries for interaction with
16320  * other DragDrop objects.</li>
16321  * <li>handle element(s): The drag operation only occurs if the element that
16322  * was clicked matches a handle element.  By default this is the linked
16323  * element, but there are times that you will want only a portion of the
16324  * linked element to initiate the drag operation, and the setHandleElId()
16325  * method provides a way to define this.</li>
16326  * <li>drag element: this represents the element that would be moved along
16327  * with the cursor during a drag operation.  By default, this is the linked
16328  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16329  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16330  * </li>
16331  * </ul>
16332  * This class should not be instantiated until the onload event to ensure that
16333  * the associated elements are available.
16334  * The following would define a DragDrop obj that would interact with any
16335  * other DragDrop obj in the "group1" group:
16336  * <pre>
16337  *  dd = new Roo.dd.DragDrop("div1", "group1");
16338  * </pre>
16339  * Since none of the event handlers have been implemented, nothing would
16340  * actually happen if you were to run the code above.  Normally you would
16341  * override this class or one of the default implementations, but you can
16342  * also override the methods you want on an instance of the class...
16343  * <pre>
16344  *  dd.onDragDrop = function(e, id) {
16345  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16346  *  }
16347  * </pre>
16348  * @constructor
16349  * @param {String} id of the element that is linked to this instance
16350  * @param {String} sGroup the group of related DragDrop objects
16351  * @param {object} config an object containing configurable attributes
16352  *                Valid properties for DragDrop:
16353  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16354  */
16355 Roo.dd.DragDrop = function(id, sGroup, config) {
16356     if (id) {
16357         this.init(id, sGroup, config);
16358     }
16359     
16360 };
16361
16362 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16363
16364     /**
16365      * The id of the element associated with this object.  This is what we
16366      * refer to as the "linked element" because the size and position of
16367      * this element is used to determine when the drag and drop objects have
16368      * interacted.
16369      * @property id
16370      * @type String
16371      */
16372     id: null,
16373
16374     /**
16375      * Configuration attributes passed into the constructor
16376      * @property config
16377      * @type object
16378      */
16379     config: null,
16380
16381     /**
16382      * The id of the element that will be dragged.  By default this is same
16383      * as the linked element , but could be changed to another element. Ex:
16384      * Roo.dd.DDProxy
16385      * @property dragElId
16386      * @type String
16387      * @private
16388      */
16389     dragElId: null,
16390
16391     /**
16392      * the id of the element that initiates the drag operation.  By default
16393      * this is the linked element, but could be changed to be a child of this
16394      * element.  This lets us do things like only starting the drag when the
16395      * header element within the linked html element is clicked.
16396      * @property handleElId
16397      * @type String
16398      * @private
16399      */
16400     handleElId: null,
16401
16402     /**
16403      * An associative array of HTML tags that will be ignored if clicked.
16404      * @property invalidHandleTypes
16405      * @type {string: string}
16406      */
16407     invalidHandleTypes: null,
16408
16409     /**
16410      * An associative array of ids for elements that will be ignored if clicked
16411      * @property invalidHandleIds
16412      * @type {string: string}
16413      */
16414     invalidHandleIds: null,
16415
16416     /**
16417      * An indexted array of css class names for elements that will be ignored
16418      * if clicked.
16419      * @property invalidHandleClasses
16420      * @type string[]
16421      */
16422     invalidHandleClasses: null,
16423
16424     /**
16425      * The linked element's absolute X position at the time the drag was
16426      * started
16427      * @property startPageX
16428      * @type int
16429      * @private
16430      */
16431     startPageX: 0,
16432
16433     /**
16434      * The linked element's absolute X position at the time the drag was
16435      * started
16436      * @property startPageY
16437      * @type int
16438      * @private
16439      */
16440     startPageY: 0,
16441
16442     /**
16443      * The group defines a logical collection of DragDrop objects that are
16444      * related.  Instances only get events when interacting with other
16445      * DragDrop object in the same group.  This lets us define multiple
16446      * groups using a single DragDrop subclass if we want.
16447      * @property groups
16448      * @type {string: string}
16449      */
16450     groups: null,
16451
16452     /**
16453      * Individual drag/drop instances can be locked.  This will prevent
16454      * onmousedown start drag.
16455      * @property locked
16456      * @type boolean
16457      * @private
16458      */
16459     locked: false,
16460
16461     /**
16462      * Lock this instance
16463      * @method lock
16464      */
16465     lock: function() { this.locked = true; },
16466
16467     /**
16468      * Unlock this instace
16469      * @method unlock
16470      */
16471     unlock: function() { this.locked = false; },
16472
16473     /**
16474      * By default, all insances can be a drop target.  This can be disabled by
16475      * setting isTarget to false.
16476      * @method isTarget
16477      * @type boolean
16478      */
16479     isTarget: true,
16480
16481     /**
16482      * The padding configured for this drag and drop object for calculating
16483      * the drop zone intersection with this object.
16484      * @method padding
16485      * @type int[]
16486      */
16487     padding: null,
16488
16489     /**
16490      * Cached reference to the linked element
16491      * @property _domRef
16492      * @private
16493      */
16494     _domRef: null,
16495
16496     /**
16497      * Internal typeof flag
16498      * @property __ygDragDrop
16499      * @private
16500      */
16501     __ygDragDrop: true,
16502
16503     /**
16504      * Set to true when horizontal contraints are applied
16505      * @property constrainX
16506      * @type boolean
16507      * @private
16508      */
16509     constrainX: false,
16510
16511     /**
16512      * Set to true when vertical contraints are applied
16513      * @property constrainY
16514      * @type boolean
16515      * @private
16516      */
16517     constrainY: false,
16518
16519     /**
16520      * The left constraint
16521      * @property minX
16522      * @type int
16523      * @private
16524      */
16525     minX: 0,
16526
16527     /**
16528      * The right constraint
16529      * @property maxX
16530      * @type int
16531      * @private
16532      */
16533     maxX: 0,
16534
16535     /**
16536      * The up constraint
16537      * @property minY
16538      * @type int
16539      * @type int
16540      * @private
16541      */
16542     minY: 0,
16543
16544     /**
16545      * The down constraint
16546      * @property maxY
16547      * @type int
16548      * @private
16549      */
16550     maxY: 0,
16551
16552     /**
16553      * Maintain offsets when we resetconstraints.  Set to true when you want
16554      * the position of the element relative to its parent to stay the same
16555      * when the page changes
16556      *
16557      * @property maintainOffset
16558      * @type boolean
16559      */
16560     maintainOffset: false,
16561
16562     /**
16563      * Array of pixel locations the element will snap to if we specified a
16564      * horizontal graduation/interval.  This array is generated automatically
16565      * when you define a tick interval.
16566      * @property xTicks
16567      * @type int[]
16568      */
16569     xTicks: null,
16570
16571     /**
16572      * Array of pixel locations the element will snap to if we specified a
16573      * vertical graduation/interval.  This array is generated automatically
16574      * when you define a tick interval.
16575      * @property yTicks
16576      * @type int[]
16577      */
16578     yTicks: null,
16579
16580     /**
16581      * By default the drag and drop instance will only respond to the primary
16582      * button click (left button for a right-handed mouse).  Set to true to
16583      * allow drag and drop to start with any mouse click that is propogated
16584      * by the browser
16585      * @property primaryButtonOnly
16586      * @type boolean
16587      */
16588     primaryButtonOnly: true,
16589
16590     /**
16591      * The availabe property is false until the linked dom element is accessible.
16592      * @property available
16593      * @type boolean
16594      */
16595     available: false,
16596
16597     /**
16598      * By default, drags can only be initiated if the mousedown occurs in the
16599      * region the linked element is.  This is done in part to work around a
16600      * bug in some browsers that mis-report the mousedown if the previous
16601      * mouseup happened outside of the window.  This property is set to true
16602      * if outer handles are defined.
16603      *
16604      * @property hasOuterHandles
16605      * @type boolean
16606      * @default false
16607      */
16608     hasOuterHandles: false,
16609
16610     /**
16611      * Code that executes immediately before the startDrag event
16612      * @method b4StartDrag
16613      * @private
16614      */
16615     b4StartDrag: function(x, y) { },
16616
16617     /**
16618      * Abstract method called after a drag/drop object is clicked
16619      * and the drag or mousedown time thresholds have beeen met.
16620      * @method startDrag
16621      * @param {int} X click location
16622      * @param {int} Y click location
16623      */
16624     startDrag: function(x, y) { /* override this */ },
16625
16626     /**
16627      * Code that executes immediately before the onDrag event
16628      * @method b4Drag
16629      * @private
16630      */
16631     b4Drag: function(e) { },
16632
16633     /**
16634      * Abstract method called during the onMouseMove event while dragging an
16635      * object.
16636      * @method onDrag
16637      * @param {Event} e the mousemove event
16638      */
16639     onDrag: function(e) { /* override this */ },
16640
16641     /**
16642      * Abstract method called when this element fist begins hovering over
16643      * another DragDrop obj
16644      * @method onDragEnter
16645      * @param {Event} e the mousemove event
16646      * @param {String|DragDrop[]} id In POINT mode, the element
16647      * id this is hovering over.  In INTERSECT mode, an array of one or more
16648      * dragdrop items being hovered over.
16649      */
16650     onDragEnter: function(e, id) { /* override this */ },
16651
16652     /**
16653      * Code that executes immediately before the onDragOver event
16654      * @method b4DragOver
16655      * @private
16656      */
16657     b4DragOver: function(e) { },
16658
16659     /**
16660      * Abstract method called when this element is hovering over another
16661      * DragDrop obj
16662      * @method onDragOver
16663      * @param {Event} e the mousemove event
16664      * @param {String|DragDrop[]} id In POINT mode, the element
16665      * id this is hovering over.  In INTERSECT mode, an array of dd items
16666      * being hovered over.
16667      */
16668     onDragOver: function(e, id) { /* override this */ },
16669
16670     /**
16671      * Code that executes immediately before the onDragOut event
16672      * @method b4DragOut
16673      * @private
16674      */
16675     b4DragOut: function(e) { },
16676
16677     /**
16678      * Abstract method called when we are no longer hovering over an element
16679      * @method onDragOut
16680      * @param {Event} e the mousemove event
16681      * @param {String|DragDrop[]} id In POINT mode, the element
16682      * id this was hovering over.  In INTERSECT mode, an array of dd items
16683      * that the mouse is no longer over.
16684      */
16685     onDragOut: function(e, id) { /* override this */ },
16686
16687     /**
16688      * Code that executes immediately before the onDragDrop event
16689      * @method b4DragDrop
16690      * @private
16691      */
16692     b4DragDrop: function(e) { },
16693
16694     /**
16695      * Abstract method called when this item is dropped on another DragDrop
16696      * obj
16697      * @method onDragDrop
16698      * @param {Event} e the mouseup event
16699      * @param {String|DragDrop[]} id In POINT mode, the element
16700      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16701      * was dropped on.
16702      */
16703     onDragDrop: function(e, id) { /* override this */ },
16704
16705     /**
16706      * Abstract method called when this item is dropped on an area with no
16707      * drop target
16708      * @method onInvalidDrop
16709      * @param {Event} e the mouseup event
16710      */
16711     onInvalidDrop: function(e) { /* override this */ },
16712
16713     /**
16714      * Code that executes immediately before the endDrag event
16715      * @method b4EndDrag
16716      * @private
16717      */
16718     b4EndDrag: function(e) { },
16719
16720     /**
16721      * Fired when we are done dragging the object
16722      * @method endDrag
16723      * @param {Event} e the mouseup event
16724      */
16725     endDrag: function(e) { /* override this */ },
16726
16727     /**
16728      * Code executed immediately before the onMouseDown event
16729      * @method b4MouseDown
16730      * @param {Event} e the mousedown event
16731      * @private
16732      */
16733     b4MouseDown: function(e) {  },
16734
16735     /**
16736      * Event handler that fires when a drag/drop obj gets a mousedown
16737      * @method onMouseDown
16738      * @param {Event} e the mousedown event
16739      */
16740     onMouseDown: function(e) { /* override this */ },
16741
16742     /**
16743      * Event handler that fires when a drag/drop obj gets a mouseup
16744      * @method onMouseUp
16745      * @param {Event} e the mouseup event
16746      */
16747     onMouseUp: function(e) { /* override this */ },
16748
16749     /**
16750      * Override the onAvailable method to do what is needed after the initial
16751      * position was determined.
16752      * @method onAvailable
16753      */
16754     onAvailable: function () {
16755     },
16756
16757     /*
16758      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16759      * @type Object
16760      */
16761     defaultPadding : {left:0, right:0, top:0, bottom:0},
16762
16763     /*
16764      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16765  *
16766  * Usage:
16767  <pre><code>
16768  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16769                 { dragElId: "existingProxyDiv" });
16770  dd.startDrag = function(){
16771      this.constrainTo("parent-id");
16772  };
16773  </code></pre>
16774  * Or you can initalize it using the {@link Roo.Element} object:
16775  <pre><code>
16776  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16777      startDrag : function(){
16778          this.constrainTo("parent-id");
16779      }
16780  });
16781  </code></pre>
16782      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16783      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16784      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16785      * an object containing the sides to pad. For example: {right:10, bottom:10}
16786      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16787      */
16788     constrainTo : function(constrainTo, pad, inContent){
16789         if(typeof pad == "number"){
16790             pad = {left: pad, right:pad, top:pad, bottom:pad};
16791         }
16792         pad = pad || this.defaultPadding;
16793         var b = Roo.get(this.getEl()).getBox();
16794         var ce = Roo.get(constrainTo);
16795         var s = ce.getScroll();
16796         var c, cd = ce.dom;
16797         if(cd == document.body){
16798             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16799         }else{
16800             xy = ce.getXY();
16801             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16802         }
16803
16804
16805         var topSpace = b.y - c.y;
16806         var leftSpace = b.x - c.x;
16807
16808         this.resetConstraints();
16809         this.setXConstraint(leftSpace - (pad.left||0), // left
16810                 c.width - leftSpace - b.width - (pad.right||0) //right
16811         );
16812         this.setYConstraint(topSpace - (pad.top||0), //top
16813                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16814         );
16815     },
16816
16817     /**
16818      * Returns a reference to the linked element
16819      * @method getEl
16820      * @return {HTMLElement} the html element
16821      */
16822     getEl: function() {
16823         if (!this._domRef) {
16824             this._domRef = Roo.getDom(this.id);
16825         }
16826
16827         return this._domRef;
16828     },
16829
16830     /**
16831      * Returns a reference to the actual element to drag.  By default this is
16832      * the same as the html element, but it can be assigned to another
16833      * element. An example of this can be found in Roo.dd.DDProxy
16834      * @method getDragEl
16835      * @return {HTMLElement} the html element
16836      */
16837     getDragEl: function() {
16838         return Roo.getDom(this.dragElId);
16839     },
16840
16841     /**
16842      * Sets up the DragDrop object.  Must be called in the constructor of any
16843      * Roo.dd.DragDrop subclass
16844      * @method init
16845      * @param id the id of the linked element
16846      * @param {String} sGroup the group of related items
16847      * @param {object} config configuration attributes
16848      */
16849     init: function(id, sGroup, config) {
16850         this.initTarget(id, sGroup, config);
16851         if (!Roo.isTouch) {
16852             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16853         }
16854         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16855         // Event.on(this.id, "selectstart", Event.preventDefault);
16856     },
16857
16858     /**
16859      * Initializes Targeting functionality only... the object does not
16860      * get a mousedown handler.
16861      * @method initTarget
16862      * @param id the id of the linked element
16863      * @param {String} sGroup the group of related items
16864      * @param {object} config configuration attributes
16865      */
16866     initTarget: function(id, sGroup, config) {
16867
16868         // configuration attributes
16869         this.config = config || {};
16870
16871         // create a local reference to the drag and drop manager
16872         this.DDM = Roo.dd.DDM;
16873         // initialize the groups array
16874         this.groups = {};
16875
16876         // assume that we have an element reference instead of an id if the
16877         // parameter is not a string
16878         if (typeof id !== "string") {
16879             id = Roo.id(id);
16880         }
16881
16882         // set the id
16883         this.id = id;
16884
16885         // add to an interaction group
16886         this.addToGroup((sGroup) ? sGroup : "default");
16887
16888         // We don't want to register this as the handle with the manager
16889         // so we just set the id rather than calling the setter.
16890         this.handleElId = id;
16891
16892         // the linked element is the element that gets dragged by default
16893         this.setDragElId(id);
16894
16895         // by default, clicked anchors will not start drag operations.
16896         this.invalidHandleTypes = { A: "A" };
16897         this.invalidHandleIds = {};
16898         this.invalidHandleClasses = [];
16899
16900         this.applyConfig();
16901
16902         this.handleOnAvailable();
16903     },
16904
16905     /**
16906      * Applies the configuration parameters that were passed into the constructor.
16907      * This is supposed to happen at each level through the inheritance chain.  So
16908      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16909      * DragDrop in order to get all of the parameters that are available in
16910      * each object.
16911      * @method applyConfig
16912      */
16913     applyConfig: function() {
16914
16915         // configurable properties:
16916         //    padding, isTarget, maintainOffset, primaryButtonOnly
16917         this.padding           = this.config.padding || [0, 0, 0, 0];
16918         this.isTarget          = (this.config.isTarget !== false);
16919         this.maintainOffset    = (this.config.maintainOffset);
16920         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16921
16922     },
16923
16924     /**
16925      * Executed when the linked element is available
16926      * @method handleOnAvailable
16927      * @private
16928      */
16929     handleOnAvailable: function() {
16930         this.available = true;
16931         this.resetConstraints();
16932         this.onAvailable();
16933     },
16934
16935      /**
16936      * Configures the padding for the target zone in px.  Effectively expands
16937      * (or reduces) the virtual object size for targeting calculations.
16938      * Supports css-style shorthand; if only one parameter is passed, all sides
16939      * will have that padding, and if only two are passed, the top and bottom
16940      * will have the first param, the left and right the second.
16941      * @method setPadding
16942      * @param {int} iTop    Top pad
16943      * @param {int} iRight  Right pad
16944      * @param {int} iBot    Bot pad
16945      * @param {int} iLeft   Left pad
16946      */
16947     setPadding: function(iTop, iRight, iBot, iLeft) {
16948         // this.padding = [iLeft, iRight, iTop, iBot];
16949         if (!iRight && 0 !== iRight) {
16950             this.padding = [iTop, iTop, iTop, iTop];
16951         } else if (!iBot && 0 !== iBot) {
16952             this.padding = [iTop, iRight, iTop, iRight];
16953         } else {
16954             this.padding = [iTop, iRight, iBot, iLeft];
16955         }
16956     },
16957
16958     /**
16959      * Stores the initial placement of the linked element.
16960      * @method setInitialPosition
16961      * @param {int} diffX   the X offset, default 0
16962      * @param {int} diffY   the Y offset, default 0
16963      */
16964     setInitPosition: function(diffX, diffY) {
16965         var el = this.getEl();
16966
16967         if (!this.DDM.verifyEl(el)) {
16968             return;
16969         }
16970
16971         var dx = diffX || 0;
16972         var dy = diffY || 0;
16973
16974         var p = Dom.getXY( el );
16975
16976         this.initPageX = p[0] - dx;
16977         this.initPageY = p[1] - dy;
16978
16979         this.lastPageX = p[0];
16980         this.lastPageY = p[1];
16981
16982
16983         this.setStartPosition(p);
16984     },
16985
16986     /**
16987      * Sets the start position of the element.  This is set when the obj
16988      * is initialized, the reset when a drag is started.
16989      * @method setStartPosition
16990      * @param pos current position (from previous lookup)
16991      * @private
16992      */
16993     setStartPosition: function(pos) {
16994         var p = pos || Dom.getXY( this.getEl() );
16995         this.deltaSetXY = null;
16996
16997         this.startPageX = p[0];
16998         this.startPageY = p[1];
16999     },
17000
17001     /**
17002      * Add this instance to a group of related drag/drop objects.  All
17003      * instances belong to at least one group, and can belong to as many
17004      * groups as needed.
17005      * @method addToGroup
17006      * @param sGroup {string} the name of the group
17007      */
17008     addToGroup: function(sGroup) {
17009         this.groups[sGroup] = true;
17010         this.DDM.regDragDrop(this, sGroup);
17011     },
17012
17013     /**
17014      * Remove's this instance from the supplied interaction group
17015      * @method removeFromGroup
17016      * @param {string}  sGroup  The group to drop
17017      */
17018     removeFromGroup: function(sGroup) {
17019         if (this.groups[sGroup]) {
17020             delete this.groups[sGroup];
17021         }
17022
17023         this.DDM.removeDDFromGroup(this, sGroup);
17024     },
17025
17026     /**
17027      * Allows you to specify that an element other than the linked element
17028      * will be moved with the cursor during a drag
17029      * @method setDragElId
17030      * @param id {string} the id of the element that will be used to initiate the drag
17031      */
17032     setDragElId: function(id) {
17033         this.dragElId = id;
17034     },
17035
17036     /**
17037      * Allows you to specify a child of the linked element that should be
17038      * used to initiate the drag operation.  An example of this would be if
17039      * you have a content div with text and links.  Clicking anywhere in the
17040      * content area would normally start the drag operation.  Use this method
17041      * to specify that an element inside of the content div is the element
17042      * that starts the drag operation.
17043      * @method setHandleElId
17044      * @param id {string} the id of the element that will be used to
17045      * initiate the drag.
17046      */
17047     setHandleElId: function(id) {
17048         if (typeof id !== "string") {
17049             id = Roo.id(id);
17050         }
17051         this.handleElId = id;
17052         this.DDM.regHandle(this.id, id);
17053     },
17054
17055     /**
17056      * Allows you to set an element outside of the linked element as a drag
17057      * handle
17058      * @method setOuterHandleElId
17059      * @param id the id of the element that will be used to initiate the drag
17060      */
17061     setOuterHandleElId: function(id) {
17062         if (typeof id !== "string") {
17063             id = Roo.id(id);
17064         }
17065         Event.on(id, "mousedown",
17066                 this.handleMouseDown, this);
17067         this.setHandleElId(id);
17068
17069         this.hasOuterHandles = true;
17070     },
17071
17072     /**
17073      * Remove all drag and drop hooks for this element
17074      * @method unreg
17075      */
17076     unreg: function() {
17077         Event.un(this.id, "mousedown",
17078                 this.handleMouseDown);
17079         Event.un(this.id, "touchstart",
17080                 this.handleMouseDown);
17081         this._domRef = null;
17082         this.DDM._remove(this);
17083     },
17084
17085     destroy : function(){
17086         this.unreg();
17087     },
17088
17089     /**
17090      * Returns true if this instance is locked, or the drag drop mgr is locked
17091      * (meaning that all drag/drop is disabled on the page.)
17092      * @method isLocked
17093      * @return {boolean} true if this obj or all drag/drop is locked, else
17094      * false
17095      */
17096     isLocked: function() {
17097         return (this.DDM.isLocked() || this.locked);
17098     },
17099
17100     /**
17101      * Fired when this object is clicked
17102      * @method handleMouseDown
17103      * @param {Event} e
17104      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17105      * @private
17106      */
17107     handleMouseDown: function(e, oDD){
17108      
17109         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17110             //Roo.log('not touch/ button !=0');
17111             return;
17112         }
17113         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17114             return; // double touch..
17115         }
17116         
17117
17118         if (this.isLocked()) {
17119             //Roo.log('locked');
17120             return;
17121         }
17122
17123         this.DDM.refreshCache(this.groups);
17124 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17125         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17126         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17127             //Roo.log('no outer handes or not over target');
17128                 // do nothing.
17129         } else {
17130 //            Roo.log('check validator');
17131             if (this.clickValidator(e)) {
17132 //                Roo.log('validate success');
17133                 // set the initial element position
17134                 this.setStartPosition();
17135
17136
17137                 this.b4MouseDown(e);
17138                 this.onMouseDown(e);
17139
17140                 this.DDM.handleMouseDown(e, this);
17141
17142                 this.DDM.stopEvent(e);
17143             } else {
17144
17145
17146             }
17147         }
17148     },
17149
17150     clickValidator: function(e) {
17151         var target = e.getTarget();
17152         return ( this.isValidHandleChild(target) &&
17153                     (this.id == this.handleElId ||
17154                         this.DDM.handleWasClicked(target, this.id)) );
17155     },
17156
17157     /**
17158      * Allows you to specify a tag name that should not start a drag operation
17159      * when clicked.  This is designed to facilitate embedding links within a
17160      * drag handle that do something other than start the drag.
17161      * @method addInvalidHandleType
17162      * @param {string} tagName the type of element to exclude
17163      */
17164     addInvalidHandleType: function(tagName) {
17165         var type = tagName.toUpperCase();
17166         this.invalidHandleTypes[type] = type;
17167     },
17168
17169     /**
17170      * Lets you to specify an element id for a child of a drag handle
17171      * that should not initiate a drag
17172      * @method addInvalidHandleId
17173      * @param {string} id the element id of the element you wish to ignore
17174      */
17175     addInvalidHandleId: function(id) {
17176         if (typeof id !== "string") {
17177             id = Roo.id(id);
17178         }
17179         this.invalidHandleIds[id] = id;
17180     },
17181
17182     /**
17183      * Lets you specify a css class of elements that will not initiate a drag
17184      * @method addInvalidHandleClass
17185      * @param {string} cssClass the class of the elements you wish to ignore
17186      */
17187     addInvalidHandleClass: function(cssClass) {
17188         this.invalidHandleClasses.push(cssClass);
17189     },
17190
17191     /**
17192      * Unsets an excluded tag name set by addInvalidHandleType
17193      * @method removeInvalidHandleType
17194      * @param {string} tagName the type of element to unexclude
17195      */
17196     removeInvalidHandleType: function(tagName) {
17197         var type = tagName.toUpperCase();
17198         // this.invalidHandleTypes[type] = null;
17199         delete this.invalidHandleTypes[type];
17200     },
17201
17202     /**
17203      * Unsets an invalid handle id
17204      * @method removeInvalidHandleId
17205      * @param {string} id the id of the element to re-enable
17206      */
17207     removeInvalidHandleId: function(id) {
17208         if (typeof id !== "string") {
17209             id = Roo.id(id);
17210         }
17211         delete this.invalidHandleIds[id];
17212     },
17213
17214     /**
17215      * Unsets an invalid css class
17216      * @method removeInvalidHandleClass
17217      * @param {string} cssClass the class of the element(s) you wish to
17218      * re-enable
17219      */
17220     removeInvalidHandleClass: function(cssClass) {
17221         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17222             if (this.invalidHandleClasses[i] == cssClass) {
17223                 delete this.invalidHandleClasses[i];
17224             }
17225         }
17226     },
17227
17228     /**
17229      * Checks the tag exclusion list to see if this click should be ignored
17230      * @method isValidHandleChild
17231      * @param {HTMLElement} node the HTMLElement to evaluate
17232      * @return {boolean} true if this is a valid tag type, false if not
17233      */
17234     isValidHandleChild: function(node) {
17235
17236         var valid = true;
17237         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17238         var nodeName;
17239         try {
17240             nodeName = node.nodeName.toUpperCase();
17241         } catch(e) {
17242             nodeName = node.nodeName;
17243         }
17244         valid = valid && !this.invalidHandleTypes[nodeName];
17245         valid = valid && !this.invalidHandleIds[node.id];
17246
17247         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17248             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17249         }
17250
17251
17252         return valid;
17253
17254     },
17255
17256     /**
17257      * Create the array of horizontal tick marks if an interval was specified
17258      * in setXConstraint().
17259      * @method setXTicks
17260      * @private
17261      */
17262     setXTicks: function(iStartX, iTickSize) {
17263         this.xTicks = [];
17264         this.xTickSize = iTickSize;
17265
17266         var tickMap = {};
17267
17268         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17269             if (!tickMap[i]) {
17270                 this.xTicks[this.xTicks.length] = i;
17271                 tickMap[i] = true;
17272             }
17273         }
17274
17275         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17276             if (!tickMap[i]) {
17277                 this.xTicks[this.xTicks.length] = i;
17278                 tickMap[i] = true;
17279             }
17280         }
17281
17282         this.xTicks.sort(this.DDM.numericSort) ;
17283     },
17284
17285     /**
17286      * Create the array of vertical tick marks if an interval was specified in
17287      * setYConstraint().
17288      * @method setYTicks
17289      * @private
17290      */
17291     setYTicks: function(iStartY, iTickSize) {
17292         this.yTicks = [];
17293         this.yTickSize = iTickSize;
17294
17295         var tickMap = {};
17296
17297         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17298             if (!tickMap[i]) {
17299                 this.yTicks[this.yTicks.length] = i;
17300                 tickMap[i] = true;
17301             }
17302         }
17303
17304         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17305             if (!tickMap[i]) {
17306                 this.yTicks[this.yTicks.length] = i;
17307                 tickMap[i] = true;
17308             }
17309         }
17310
17311         this.yTicks.sort(this.DDM.numericSort) ;
17312     },
17313
17314     /**
17315      * By default, the element can be dragged any place on the screen.  Use
17316      * this method to limit the horizontal travel of the element.  Pass in
17317      * 0,0 for the parameters if you want to lock the drag to the y axis.
17318      * @method setXConstraint
17319      * @param {int} iLeft the number of pixels the element can move to the left
17320      * @param {int} iRight the number of pixels the element can move to the
17321      * right
17322      * @param {int} iTickSize optional parameter for specifying that the
17323      * element
17324      * should move iTickSize pixels at a time.
17325      */
17326     setXConstraint: function(iLeft, iRight, iTickSize) {
17327         this.leftConstraint = iLeft;
17328         this.rightConstraint = iRight;
17329
17330         this.minX = this.initPageX - iLeft;
17331         this.maxX = this.initPageX + iRight;
17332         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17333
17334         this.constrainX = true;
17335     },
17336
17337     /**
17338      * Clears any constraints applied to this instance.  Also clears ticks
17339      * since they can't exist independent of a constraint at this time.
17340      * @method clearConstraints
17341      */
17342     clearConstraints: function() {
17343         this.constrainX = false;
17344         this.constrainY = false;
17345         this.clearTicks();
17346     },
17347
17348     /**
17349      * Clears any tick interval defined for this instance
17350      * @method clearTicks
17351      */
17352     clearTicks: function() {
17353         this.xTicks = null;
17354         this.yTicks = null;
17355         this.xTickSize = 0;
17356         this.yTickSize = 0;
17357     },
17358
17359     /**
17360      * By default, the element can be dragged any place on the screen.  Set
17361      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17362      * parameters if you want to lock the drag to the x axis.
17363      * @method setYConstraint
17364      * @param {int} iUp the number of pixels the element can move up
17365      * @param {int} iDown the number of pixels the element can move down
17366      * @param {int} iTickSize optional parameter for specifying that the
17367      * element should move iTickSize pixels at a time.
17368      */
17369     setYConstraint: function(iUp, iDown, iTickSize) {
17370         this.topConstraint = iUp;
17371         this.bottomConstraint = iDown;
17372
17373         this.minY = this.initPageY - iUp;
17374         this.maxY = this.initPageY + iDown;
17375         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17376
17377         this.constrainY = true;
17378
17379     },
17380
17381     /**
17382      * resetConstraints must be called if you manually reposition a dd element.
17383      * @method resetConstraints
17384      * @param {boolean} maintainOffset
17385      */
17386     resetConstraints: function() {
17387
17388
17389         // Maintain offsets if necessary
17390         if (this.initPageX || this.initPageX === 0) {
17391             // figure out how much this thing has moved
17392             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17393             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17394
17395             this.setInitPosition(dx, dy);
17396
17397         // This is the first time we have detected the element's position
17398         } else {
17399             this.setInitPosition();
17400         }
17401
17402         if (this.constrainX) {
17403             this.setXConstraint( this.leftConstraint,
17404                                  this.rightConstraint,
17405                                  this.xTickSize        );
17406         }
17407
17408         if (this.constrainY) {
17409             this.setYConstraint( this.topConstraint,
17410                                  this.bottomConstraint,
17411                                  this.yTickSize         );
17412         }
17413     },
17414
17415     /**
17416      * Normally the drag element is moved pixel by pixel, but we can specify
17417      * that it move a number of pixels at a time.  This method resolves the
17418      * location when we have it set up like this.
17419      * @method getTick
17420      * @param {int} val where we want to place the object
17421      * @param {int[]} tickArray sorted array of valid points
17422      * @return {int} the closest tick
17423      * @private
17424      */
17425     getTick: function(val, tickArray) {
17426
17427         if (!tickArray) {
17428             // If tick interval is not defined, it is effectively 1 pixel,
17429             // so we return the value passed to us.
17430             return val;
17431         } else if (tickArray[0] >= val) {
17432             // The value is lower than the first tick, so we return the first
17433             // tick.
17434             return tickArray[0];
17435         } else {
17436             for (var i=0, len=tickArray.length; i<len; ++i) {
17437                 var next = i + 1;
17438                 if (tickArray[next] && tickArray[next] >= val) {
17439                     var diff1 = val - tickArray[i];
17440                     var diff2 = tickArray[next] - val;
17441                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17442                 }
17443             }
17444
17445             // The value is larger than the last tick, so we return the last
17446             // tick.
17447             return tickArray[tickArray.length - 1];
17448         }
17449     },
17450
17451     /**
17452      * toString method
17453      * @method toString
17454      * @return {string} string representation of the dd obj
17455      */
17456     toString: function() {
17457         return ("DragDrop " + this.id);
17458     }
17459
17460 });
17461
17462 })();
17463 /*
17464  * Based on:
17465  * Ext JS Library 1.1.1
17466  * Copyright(c) 2006-2007, Ext JS, LLC.
17467  *
17468  * Originally Released Under LGPL - original licence link has changed is not relivant.
17469  *
17470  * Fork - LGPL
17471  * <script type="text/javascript">
17472  */
17473
17474
17475 /**
17476  * The drag and drop utility provides a framework for building drag and drop
17477  * applications.  In addition to enabling drag and drop for specific elements,
17478  * the drag and drop elements are tracked by the manager class, and the
17479  * interactions between the various elements are tracked during the drag and
17480  * the implementing code is notified about these important moments.
17481  */
17482
17483 // Only load the library once.  Rewriting the manager class would orphan
17484 // existing drag and drop instances.
17485 if (!Roo.dd.DragDropMgr) {
17486
17487 /**
17488  * @class Roo.dd.DragDropMgr
17489  * DragDropMgr is a singleton that tracks the element interaction for
17490  * all DragDrop items in the window.  Generally, you will not call
17491  * this class directly, but it does have helper methods that could
17492  * be useful in your DragDrop implementations.
17493  * @singleton
17494  */
17495 Roo.dd.DragDropMgr = function() {
17496
17497     var Event = Roo.EventManager;
17498
17499     return {
17500
17501         /**
17502          * Two dimensional Array of registered DragDrop objects.  The first
17503          * dimension is the DragDrop item group, the second the DragDrop
17504          * object.
17505          * @property ids
17506          * @type {string: string}
17507          * @private
17508          * @static
17509          */
17510         ids: {},
17511
17512         /**
17513          * Array of element ids defined as drag handles.  Used to determine
17514          * if the element that generated the mousedown event is actually the
17515          * handle and not the html element itself.
17516          * @property handleIds
17517          * @type {string: string}
17518          * @private
17519          * @static
17520          */
17521         handleIds: {},
17522
17523         /**
17524          * the DragDrop object that is currently being dragged
17525          * @property dragCurrent
17526          * @type DragDrop
17527          * @private
17528          * @static
17529          **/
17530         dragCurrent: null,
17531
17532         /**
17533          * the DragDrop object(s) that are being hovered over
17534          * @property dragOvers
17535          * @type Array
17536          * @private
17537          * @static
17538          */
17539         dragOvers: {},
17540
17541         /**
17542          * the X distance between the cursor and the object being dragged
17543          * @property deltaX
17544          * @type int
17545          * @private
17546          * @static
17547          */
17548         deltaX: 0,
17549
17550         /**
17551          * the Y distance between the cursor and the object being dragged
17552          * @property deltaY
17553          * @type int
17554          * @private
17555          * @static
17556          */
17557         deltaY: 0,
17558
17559         /**
17560          * Flag to determine if we should prevent the default behavior of the
17561          * events we define. By default this is true, but this can be set to
17562          * false if you need the default behavior (not recommended)
17563          * @property preventDefault
17564          * @type boolean
17565          * @static
17566          */
17567         preventDefault: true,
17568
17569         /**
17570          * Flag to determine if we should stop the propagation of the events
17571          * we generate. This is true by default but you may want to set it to
17572          * false if the html element contains other features that require the
17573          * mouse click.
17574          * @property stopPropagation
17575          * @type boolean
17576          * @static
17577          */
17578         stopPropagation: true,
17579
17580         /**
17581          * Internal flag that is set to true when drag and drop has been
17582          * intialized
17583          * @property initialized
17584          * @private
17585          * @static
17586          */
17587         initalized: false,
17588
17589         /**
17590          * All drag and drop can be disabled.
17591          * @property locked
17592          * @private
17593          * @static
17594          */
17595         locked: false,
17596
17597         /**
17598          * Called the first time an element is registered.
17599          * @method init
17600          * @private
17601          * @static
17602          */
17603         init: function() {
17604             this.initialized = true;
17605         },
17606
17607         /**
17608          * In point mode, drag and drop interaction is defined by the
17609          * location of the cursor during the drag/drop
17610          * @property POINT
17611          * @type int
17612          * @static
17613          */
17614         POINT: 0,
17615
17616         /**
17617          * In intersect mode, drag and drop interactio nis defined by the
17618          * overlap of two or more drag and drop objects.
17619          * @property INTERSECT
17620          * @type int
17621          * @static
17622          */
17623         INTERSECT: 1,
17624
17625         /**
17626          * The current drag and drop mode.  Default: POINT
17627          * @property mode
17628          * @type int
17629          * @static
17630          */
17631         mode: 0,
17632
17633         /**
17634          * Runs method on all drag and drop objects
17635          * @method _execOnAll
17636          * @private
17637          * @static
17638          */
17639         _execOnAll: function(sMethod, args) {
17640             for (var i in this.ids) {
17641                 for (var j in this.ids[i]) {
17642                     var oDD = this.ids[i][j];
17643                     if (! this.isTypeOfDD(oDD)) {
17644                         continue;
17645                     }
17646                     oDD[sMethod].apply(oDD, args);
17647                 }
17648             }
17649         },
17650
17651         /**
17652          * Drag and drop initialization.  Sets up the global event handlers
17653          * @method _onLoad
17654          * @private
17655          * @static
17656          */
17657         _onLoad: function() {
17658
17659             this.init();
17660
17661             if (!Roo.isTouch) {
17662                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17663                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17664             }
17665             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17666             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17667             
17668             Event.on(window,   "unload",    this._onUnload, this, true);
17669             Event.on(window,   "resize",    this._onResize, this, true);
17670             // Event.on(window,   "mouseout",    this._test);
17671
17672         },
17673
17674         /**
17675          * Reset constraints on all drag and drop objs
17676          * @method _onResize
17677          * @private
17678          * @static
17679          */
17680         _onResize: function(e) {
17681             this._execOnAll("resetConstraints", []);
17682         },
17683
17684         /**
17685          * Lock all drag and drop functionality
17686          * @method lock
17687          * @static
17688          */
17689         lock: function() { this.locked = true; },
17690
17691         /**
17692          * Unlock all drag and drop functionality
17693          * @method unlock
17694          * @static
17695          */
17696         unlock: function() { this.locked = false; },
17697
17698         /**
17699          * Is drag and drop locked?
17700          * @method isLocked
17701          * @return {boolean} True if drag and drop is locked, false otherwise.
17702          * @static
17703          */
17704         isLocked: function() { return this.locked; },
17705
17706         /**
17707          * Location cache that is set for all drag drop objects when a drag is
17708          * initiated, cleared when the drag is finished.
17709          * @property locationCache
17710          * @private
17711          * @static
17712          */
17713         locationCache: {},
17714
17715         /**
17716          * Set useCache to false if you want to force object the lookup of each
17717          * drag and drop linked element constantly during a drag.
17718          * @property useCache
17719          * @type boolean
17720          * @static
17721          */
17722         useCache: true,
17723
17724         /**
17725          * The number of pixels that the mouse needs to move after the
17726          * mousedown before the drag is initiated.  Default=3;
17727          * @property clickPixelThresh
17728          * @type int
17729          * @static
17730          */
17731         clickPixelThresh: 3,
17732
17733         /**
17734          * The number of milliseconds after the mousedown event to initiate the
17735          * drag if we don't get a mouseup event. Default=1000
17736          * @property clickTimeThresh
17737          * @type int
17738          * @static
17739          */
17740         clickTimeThresh: 350,
17741
17742         /**
17743          * Flag that indicates that either the drag pixel threshold or the
17744          * mousdown time threshold has been met
17745          * @property dragThreshMet
17746          * @type boolean
17747          * @private
17748          * @static
17749          */
17750         dragThreshMet: false,
17751
17752         /**
17753          * Timeout used for the click time threshold
17754          * @property clickTimeout
17755          * @type Object
17756          * @private
17757          * @static
17758          */
17759         clickTimeout: null,
17760
17761         /**
17762          * The X position of the mousedown event stored for later use when a
17763          * drag threshold is met.
17764          * @property startX
17765          * @type int
17766          * @private
17767          * @static
17768          */
17769         startX: 0,
17770
17771         /**
17772          * The Y position of the mousedown event stored for later use when a
17773          * drag threshold is met.
17774          * @property startY
17775          * @type int
17776          * @private
17777          * @static
17778          */
17779         startY: 0,
17780
17781         /**
17782          * Each DragDrop instance must be registered with the DragDropMgr.
17783          * This is executed in DragDrop.init()
17784          * @method regDragDrop
17785          * @param {DragDrop} oDD the DragDrop object to register
17786          * @param {String} sGroup the name of the group this element belongs to
17787          * @static
17788          */
17789         regDragDrop: function(oDD, sGroup) {
17790             if (!this.initialized) { this.init(); }
17791
17792             if (!this.ids[sGroup]) {
17793                 this.ids[sGroup] = {};
17794             }
17795             this.ids[sGroup][oDD.id] = oDD;
17796         },
17797
17798         /**
17799          * Removes the supplied dd instance from the supplied group. Executed
17800          * by DragDrop.removeFromGroup, so don't call this function directly.
17801          * @method removeDDFromGroup
17802          * @private
17803          * @static
17804          */
17805         removeDDFromGroup: function(oDD, sGroup) {
17806             if (!this.ids[sGroup]) {
17807                 this.ids[sGroup] = {};
17808             }
17809
17810             var obj = this.ids[sGroup];
17811             if (obj && obj[oDD.id]) {
17812                 delete obj[oDD.id];
17813             }
17814         },
17815
17816         /**
17817          * Unregisters a drag and drop item.  This is executed in
17818          * DragDrop.unreg, use that method instead of calling this directly.
17819          * @method _remove
17820          * @private
17821          * @static
17822          */
17823         _remove: function(oDD) {
17824             for (var g in oDD.groups) {
17825                 if (g && this.ids[g][oDD.id]) {
17826                     delete this.ids[g][oDD.id];
17827                 }
17828             }
17829             delete this.handleIds[oDD.id];
17830         },
17831
17832         /**
17833          * Each DragDrop handle element must be registered.  This is done
17834          * automatically when executing DragDrop.setHandleElId()
17835          * @method regHandle
17836          * @param {String} sDDId the DragDrop id this element is a handle for
17837          * @param {String} sHandleId the id of the element that is the drag
17838          * handle
17839          * @static
17840          */
17841         regHandle: function(sDDId, sHandleId) {
17842             if (!this.handleIds[sDDId]) {
17843                 this.handleIds[sDDId] = {};
17844             }
17845             this.handleIds[sDDId][sHandleId] = sHandleId;
17846         },
17847
17848         /**
17849          * Utility function to determine if a given element has been
17850          * registered as a drag drop item.
17851          * @method isDragDrop
17852          * @param {String} id the element id to check
17853          * @return {boolean} true if this element is a DragDrop item,
17854          * false otherwise
17855          * @static
17856          */
17857         isDragDrop: function(id) {
17858             return ( this.getDDById(id) ) ? true : false;
17859         },
17860
17861         /**
17862          * Returns the drag and drop instances that are in all groups the
17863          * passed in instance belongs to.
17864          * @method getRelated
17865          * @param {DragDrop} p_oDD the obj to get related data for
17866          * @param {boolean} bTargetsOnly if true, only return targetable objs
17867          * @return {DragDrop[]} the related instances
17868          * @static
17869          */
17870         getRelated: function(p_oDD, bTargetsOnly) {
17871             var oDDs = [];
17872             for (var i in p_oDD.groups) {
17873                 for (j in this.ids[i]) {
17874                     var dd = this.ids[i][j];
17875                     if (! this.isTypeOfDD(dd)) {
17876                         continue;
17877                     }
17878                     if (!bTargetsOnly || dd.isTarget) {
17879                         oDDs[oDDs.length] = dd;
17880                     }
17881                 }
17882             }
17883
17884             return oDDs;
17885         },
17886
17887         /**
17888          * Returns true if the specified dd target is a legal target for
17889          * the specifice drag obj
17890          * @method isLegalTarget
17891          * @param {DragDrop} the drag obj
17892          * @param {DragDrop} the target
17893          * @return {boolean} true if the target is a legal target for the
17894          * dd obj
17895          * @static
17896          */
17897         isLegalTarget: function (oDD, oTargetDD) {
17898             var targets = this.getRelated(oDD, true);
17899             for (var i=0, len=targets.length;i<len;++i) {
17900                 if (targets[i].id == oTargetDD.id) {
17901                     return true;
17902                 }
17903             }
17904
17905             return false;
17906         },
17907
17908         /**
17909          * My goal is to be able to transparently determine if an object is
17910          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17911          * returns "object", oDD.constructor.toString() always returns
17912          * "DragDrop" and not the name of the subclass.  So for now it just
17913          * evaluates a well-known variable in DragDrop.
17914          * @method isTypeOfDD
17915          * @param {Object} the object to evaluate
17916          * @return {boolean} true if typeof oDD = DragDrop
17917          * @static
17918          */
17919         isTypeOfDD: function (oDD) {
17920             return (oDD && oDD.__ygDragDrop);
17921         },
17922
17923         /**
17924          * Utility function to determine if a given element has been
17925          * registered as a drag drop handle for the given Drag Drop object.
17926          * @method isHandle
17927          * @param {String} id the element id to check
17928          * @return {boolean} true if this element is a DragDrop handle, false
17929          * otherwise
17930          * @static
17931          */
17932         isHandle: function(sDDId, sHandleId) {
17933             return ( this.handleIds[sDDId] &&
17934                             this.handleIds[sDDId][sHandleId] );
17935         },
17936
17937         /**
17938          * Returns the DragDrop instance for a given id
17939          * @method getDDById
17940          * @param {String} id the id of the DragDrop object
17941          * @return {DragDrop} the drag drop object, null if it is not found
17942          * @static
17943          */
17944         getDDById: function(id) {
17945             for (var i in this.ids) {
17946                 if (this.ids[i][id]) {
17947                     return this.ids[i][id];
17948                 }
17949             }
17950             return null;
17951         },
17952
17953         /**
17954          * Fired after a registered DragDrop object gets the mousedown event.
17955          * Sets up the events required to track the object being dragged
17956          * @method handleMouseDown
17957          * @param {Event} e the event
17958          * @param oDD the DragDrop object being dragged
17959          * @private
17960          * @static
17961          */
17962         handleMouseDown: function(e, oDD) {
17963             if(Roo.QuickTips){
17964                 Roo.QuickTips.disable();
17965             }
17966             this.currentTarget = e.getTarget();
17967
17968             this.dragCurrent = oDD;
17969
17970             var el = oDD.getEl();
17971
17972             // track start position
17973             this.startX = e.getPageX();
17974             this.startY = e.getPageY();
17975
17976             this.deltaX = this.startX - el.offsetLeft;
17977             this.deltaY = this.startY - el.offsetTop;
17978
17979             this.dragThreshMet = false;
17980
17981             this.clickTimeout = setTimeout(
17982                     function() {
17983                         var DDM = Roo.dd.DDM;
17984                         DDM.startDrag(DDM.startX, DDM.startY);
17985                     },
17986                     this.clickTimeThresh );
17987         },
17988
17989         /**
17990          * Fired when either the drag pixel threshol or the mousedown hold
17991          * time threshold has been met.
17992          * @method startDrag
17993          * @param x {int} the X position of the original mousedown
17994          * @param y {int} the Y position of the original mousedown
17995          * @static
17996          */
17997         startDrag: function(x, y) {
17998             clearTimeout(this.clickTimeout);
17999             if (this.dragCurrent) {
18000                 this.dragCurrent.b4StartDrag(x, y);
18001                 this.dragCurrent.startDrag(x, y);
18002             }
18003             this.dragThreshMet = true;
18004         },
18005
18006         /**
18007          * Internal function to handle the mouseup event.  Will be invoked
18008          * from the context of the document.
18009          * @method handleMouseUp
18010          * @param {Event} e the event
18011          * @private
18012          * @static
18013          */
18014         handleMouseUp: function(e) {
18015
18016             if(Roo.QuickTips){
18017                 Roo.QuickTips.enable();
18018             }
18019             if (! this.dragCurrent) {
18020                 return;
18021             }
18022
18023             clearTimeout(this.clickTimeout);
18024
18025             if (this.dragThreshMet) {
18026                 this.fireEvents(e, true);
18027             } else {
18028             }
18029
18030             this.stopDrag(e);
18031
18032             this.stopEvent(e);
18033         },
18034
18035         /**
18036          * Utility to stop event propagation and event default, if these
18037          * features are turned on.
18038          * @method stopEvent
18039          * @param {Event} e the event as returned by this.getEvent()
18040          * @static
18041          */
18042         stopEvent: function(e){
18043             if(this.stopPropagation) {
18044                 e.stopPropagation();
18045             }
18046
18047             if (this.preventDefault) {
18048                 e.preventDefault();
18049             }
18050         },
18051
18052         /**
18053          * Internal function to clean up event handlers after the drag
18054          * operation is complete
18055          * @method stopDrag
18056          * @param {Event} e the event
18057          * @private
18058          * @static
18059          */
18060         stopDrag: function(e) {
18061             // Fire the drag end event for the item that was dragged
18062             if (this.dragCurrent) {
18063                 if (this.dragThreshMet) {
18064                     this.dragCurrent.b4EndDrag(e);
18065                     this.dragCurrent.endDrag(e);
18066                 }
18067
18068                 this.dragCurrent.onMouseUp(e);
18069             }
18070
18071             this.dragCurrent = null;
18072             this.dragOvers = {};
18073         },
18074
18075         /**
18076          * Internal function to handle the mousemove event.  Will be invoked
18077          * from the context of the html element.
18078          *
18079          * @TODO figure out what we can do about mouse events lost when the
18080          * user drags objects beyond the window boundary.  Currently we can
18081          * detect this in internet explorer by verifying that the mouse is
18082          * down during the mousemove event.  Firefox doesn't give us the
18083          * button state on the mousemove event.
18084          * @method handleMouseMove
18085          * @param {Event} e the event
18086          * @private
18087          * @static
18088          */
18089         handleMouseMove: function(e) {
18090             if (! this.dragCurrent) {
18091                 return true;
18092             }
18093
18094             // var button = e.which || e.button;
18095
18096             // check for IE mouseup outside of page boundary
18097             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18098                 this.stopEvent(e);
18099                 return this.handleMouseUp(e);
18100             }
18101
18102             if (!this.dragThreshMet) {
18103                 var diffX = Math.abs(this.startX - e.getPageX());
18104                 var diffY = Math.abs(this.startY - e.getPageY());
18105                 if (diffX > this.clickPixelThresh ||
18106                             diffY > this.clickPixelThresh) {
18107                     this.startDrag(this.startX, this.startY);
18108                 }
18109             }
18110
18111             if (this.dragThreshMet) {
18112                 this.dragCurrent.b4Drag(e);
18113                 this.dragCurrent.onDrag(e);
18114                 if(!this.dragCurrent.moveOnly){
18115                     this.fireEvents(e, false);
18116                 }
18117             }
18118
18119             this.stopEvent(e);
18120
18121             return true;
18122         },
18123
18124         /**
18125          * Iterates over all of the DragDrop elements to find ones we are
18126          * hovering over or dropping on
18127          * @method fireEvents
18128          * @param {Event} e the event
18129          * @param {boolean} isDrop is this a drop op or a mouseover op?
18130          * @private
18131          * @static
18132          */
18133         fireEvents: function(e, isDrop) {
18134             var dc = this.dragCurrent;
18135
18136             // If the user did the mouse up outside of the window, we could
18137             // get here even though we have ended the drag.
18138             if (!dc || dc.isLocked()) {
18139                 return;
18140             }
18141
18142             var pt = e.getPoint();
18143
18144             // cache the previous dragOver array
18145             var oldOvers = [];
18146
18147             var outEvts   = [];
18148             var overEvts  = [];
18149             var dropEvts  = [];
18150             var enterEvts = [];
18151
18152             // Check to see if the object(s) we were hovering over is no longer
18153             // being hovered over so we can fire the onDragOut event
18154             for (var i in this.dragOvers) {
18155
18156                 var ddo = this.dragOvers[i];
18157
18158                 if (! this.isTypeOfDD(ddo)) {
18159                     continue;
18160                 }
18161
18162                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18163                     outEvts.push( ddo );
18164                 }
18165
18166                 oldOvers[i] = true;
18167                 delete this.dragOvers[i];
18168             }
18169
18170             for (var sGroup in dc.groups) {
18171
18172                 if ("string" != typeof sGroup) {
18173                     continue;
18174                 }
18175
18176                 for (i in this.ids[sGroup]) {
18177                     var oDD = this.ids[sGroup][i];
18178                     if (! this.isTypeOfDD(oDD)) {
18179                         continue;
18180                     }
18181
18182                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18183                         if (this.isOverTarget(pt, oDD, this.mode)) {
18184                             // look for drop interactions
18185                             if (isDrop) {
18186                                 dropEvts.push( oDD );
18187                             // look for drag enter and drag over interactions
18188                             } else {
18189
18190                                 // initial drag over: dragEnter fires
18191                                 if (!oldOvers[oDD.id]) {
18192                                     enterEvts.push( oDD );
18193                                 // subsequent drag overs: dragOver fires
18194                                 } else {
18195                                     overEvts.push( oDD );
18196                                 }
18197
18198                                 this.dragOvers[oDD.id] = oDD;
18199                             }
18200                         }
18201                     }
18202                 }
18203             }
18204
18205             if (this.mode) {
18206                 if (outEvts.length) {
18207                     dc.b4DragOut(e, outEvts);
18208                     dc.onDragOut(e, outEvts);
18209                 }
18210
18211                 if (enterEvts.length) {
18212                     dc.onDragEnter(e, enterEvts);
18213                 }
18214
18215                 if (overEvts.length) {
18216                     dc.b4DragOver(e, overEvts);
18217                     dc.onDragOver(e, overEvts);
18218                 }
18219
18220                 if (dropEvts.length) {
18221                     dc.b4DragDrop(e, dropEvts);
18222                     dc.onDragDrop(e, dropEvts);
18223                 }
18224
18225             } else {
18226                 // fire dragout events
18227                 var len = 0;
18228                 for (i=0, len=outEvts.length; i<len; ++i) {
18229                     dc.b4DragOut(e, outEvts[i].id);
18230                     dc.onDragOut(e, outEvts[i].id);
18231                 }
18232
18233                 // fire enter events
18234                 for (i=0,len=enterEvts.length; i<len; ++i) {
18235                     // dc.b4DragEnter(e, oDD.id);
18236                     dc.onDragEnter(e, enterEvts[i].id);
18237                 }
18238
18239                 // fire over events
18240                 for (i=0,len=overEvts.length; i<len; ++i) {
18241                     dc.b4DragOver(e, overEvts[i].id);
18242                     dc.onDragOver(e, overEvts[i].id);
18243                 }
18244
18245                 // fire drop events
18246                 for (i=0, len=dropEvts.length; i<len; ++i) {
18247                     dc.b4DragDrop(e, dropEvts[i].id);
18248                     dc.onDragDrop(e, dropEvts[i].id);
18249                 }
18250
18251             }
18252
18253             // notify about a drop that did not find a target
18254             if (isDrop && !dropEvts.length) {
18255                 dc.onInvalidDrop(e);
18256             }
18257
18258         },
18259
18260         /**
18261          * Helper function for getting the best match from the list of drag
18262          * and drop objects returned by the drag and drop events when we are
18263          * in INTERSECT mode.  It returns either the first object that the
18264          * cursor is over, or the object that has the greatest overlap with
18265          * the dragged element.
18266          * @method getBestMatch
18267          * @param  {DragDrop[]} dds The array of drag and drop objects
18268          * targeted
18269          * @return {DragDrop}       The best single match
18270          * @static
18271          */
18272         getBestMatch: function(dds) {
18273             var winner = null;
18274             // Return null if the input is not what we expect
18275             //if (!dds || !dds.length || dds.length == 0) {
18276                // winner = null;
18277             // If there is only one item, it wins
18278             //} else if (dds.length == 1) {
18279
18280             var len = dds.length;
18281
18282             if (len == 1) {
18283                 winner = dds[0];
18284             } else {
18285                 // Loop through the targeted items
18286                 for (var i=0; i<len; ++i) {
18287                     var dd = dds[i];
18288                     // If the cursor is over the object, it wins.  If the
18289                     // cursor is over multiple matches, the first one we come
18290                     // to wins.
18291                     if (dd.cursorIsOver) {
18292                         winner = dd;
18293                         break;
18294                     // Otherwise the object with the most overlap wins
18295                     } else {
18296                         if (!winner ||
18297                             winner.overlap.getArea() < dd.overlap.getArea()) {
18298                             winner = dd;
18299                         }
18300                     }
18301                 }
18302             }
18303
18304             return winner;
18305         },
18306
18307         /**
18308          * Refreshes the cache of the top-left and bottom-right points of the
18309          * drag and drop objects in the specified group(s).  This is in the
18310          * format that is stored in the drag and drop instance, so typical
18311          * usage is:
18312          * <code>
18313          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18314          * </code>
18315          * Alternatively:
18316          * <code>
18317          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18318          * </code>
18319          * @TODO this really should be an indexed array.  Alternatively this
18320          * method could accept both.
18321          * @method refreshCache
18322          * @param {Object} groups an associative array of groups to refresh
18323          * @static
18324          */
18325         refreshCache: function(groups) {
18326             for (var sGroup in groups) {
18327                 if ("string" != typeof sGroup) {
18328                     continue;
18329                 }
18330                 for (var i in this.ids[sGroup]) {
18331                     var oDD = this.ids[sGroup][i];
18332
18333                     if (this.isTypeOfDD(oDD)) {
18334                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18335                         var loc = this.getLocation(oDD);
18336                         if (loc) {
18337                             this.locationCache[oDD.id] = loc;
18338                         } else {
18339                             delete this.locationCache[oDD.id];
18340                             // this will unregister the drag and drop object if
18341                             // the element is not in a usable state
18342                             // oDD.unreg();
18343                         }
18344                     }
18345                 }
18346             }
18347         },
18348
18349         /**
18350          * This checks to make sure an element exists and is in the DOM.  The
18351          * main purpose is to handle cases where innerHTML is used to remove
18352          * drag and drop objects from the DOM.  IE provides an 'unspecified
18353          * error' when trying to access the offsetParent of such an element
18354          * @method verifyEl
18355          * @param {HTMLElement} el the element to check
18356          * @return {boolean} true if the element looks usable
18357          * @static
18358          */
18359         verifyEl: function(el) {
18360             if (el) {
18361                 var parent;
18362                 if(Roo.isIE){
18363                     try{
18364                         parent = el.offsetParent;
18365                     }catch(e){}
18366                 }else{
18367                     parent = el.offsetParent;
18368                 }
18369                 if (parent) {
18370                     return true;
18371                 }
18372             }
18373
18374             return false;
18375         },
18376
18377         /**
18378          * Returns a Region object containing the drag and drop element's position
18379          * and size, including the padding configured for it
18380          * @method getLocation
18381          * @param {DragDrop} oDD the drag and drop object to get the
18382          *                       location for
18383          * @return {Roo.lib.Region} a Region object representing the total area
18384          *                             the element occupies, including any padding
18385          *                             the instance is configured for.
18386          * @static
18387          */
18388         getLocation: function(oDD) {
18389             if (! this.isTypeOfDD(oDD)) {
18390                 return null;
18391             }
18392
18393             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18394
18395             try {
18396                 pos= Roo.lib.Dom.getXY(el);
18397             } catch (e) { }
18398
18399             if (!pos) {
18400                 return null;
18401             }
18402
18403             x1 = pos[0];
18404             x2 = x1 + el.offsetWidth;
18405             y1 = pos[1];
18406             y2 = y1 + el.offsetHeight;
18407
18408             t = y1 - oDD.padding[0];
18409             r = x2 + oDD.padding[1];
18410             b = y2 + oDD.padding[2];
18411             l = x1 - oDD.padding[3];
18412
18413             return new Roo.lib.Region( t, r, b, l );
18414         },
18415
18416         /**
18417          * Checks the cursor location to see if it over the target
18418          * @method isOverTarget
18419          * @param {Roo.lib.Point} pt The point to evaluate
18420          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18421          * @return {boolean} true if the mouse is over the target
18422          * @private
18423          * @static
18424          */
18425         isOverTarget: function(pt, oTarget, intersect) {
18426             // use cache if available
18427             var loc = this.locationCache[oTarget.id];
18428             if (!loc || !this.useCache) {
18429                 loc = this.getLocation(oTarget);
18430                 this.locationCache[oTarget.id] = loc;
18431
18432             }
18433
18434             if (!loc) {
18435                 return false;
18436             }
18437
18438             oTarget.cursorIsOver = loc.contains( pt );
18439
18440             // DragDrop is using this as a sanity check for the initial mousedown
18441             // in this case we are done.  In POINT mode, if the drag obj has no
18442             // contraints, we are also done. Otherwise we need to evaluate the
18443             // location of the target as related to the actual location of the
18444             // dragged element.
18445             var dc = this.dragCurrent;
18446             if (!dc || !dc.getTargetCoord ||
18447                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18448                 return oTarget.cursorIsOver;
18449             }
18450
18451             oTarget.overlap = null;
18452
18453             // Get the current location of the drag element, this is the
18454             // location of the mouse event less the delta that represents
18455             // where the original mousedown happened on the element.  We
18456             // need to consider constraints and ticks as well.
18457             var pos = dc.getTargetCoord(pt.x, pt.y);
18458
18459             var el = dc.getDragEl();
18460             var curRegion = new Roo.lib.Region( pos.y,
18461                                                    pos.x + el.offsetWidth,
18462                                                    pos.y + el.offsetHeight,
18463                                                    pos.x );
18464
18465             var overlap = curRegion.intersect(loc);
18466
18467             if (overlap) {
18468                 oTarget.overlap = overlap;
18469                 return (intersect) ? true : oTarget.cursorIsOver;
18470             } else {
18471                 return false;
18472             }
18473         },
18474
18475         /**
18476          * unload event handler
18477          * @method _onUnload
18478          * @private
18479          * @static
18480          */
18481         _onUnload: function(e, me) {
18482             Roo.dd.DragDropMgr.unregAll();
18483         },
18484
18485         /**
18486          * Cleans up the drag and drop events and objects.
18487          * @method unregAll
18488          * @private
18489          * @static
18490          */
18491         unregAll: function() {
18492
18493             if (this.dragCurrent) {
18494                 this.stopDrag();
18495                 this.dragCurrent = null;
18496             }
18497
18498             this._execOnAll("unreg", []);
18499
18500             for (i in this.elementCache) {
18501                 delete this.elementCache[i];
18502             }
18503
18504             this.elementCache = {};
18505             this.ids = {};
18506         },
18507
18508         /**
18509          * A cache of DOM elements
18510          * @property elementCache
18511          * @private
18512          * @static
18513          */
18514         elementCache: {},
18515
18516         /**
18517          * Get the wrapper for the DOM element specified
18518          * @method getElWrapper
18519          * @param {String} id the id of the element to get
18520          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18521          * @private
18522          * @deprecated This wrapper isn't that useful
18523          * @static
18524          */
18525         getElWrapper: function(id) {
18526             var oWrapper = this.elementCache[id];
18527             if (!oWrapper || !oWrapper.el) {
18528                 oWrapper = this.elementCache[id] =
18529                     new this.ElementWrapper(Roo.getDom(id));
18530             }
18531             return oWrapper;
18532         },
18533
18534         /**
18535          * Returns the actual DOM element
18536          * @method getElement
18537          * @param {String} id the id of the elment to get
18538          * @return {Object} The element
18539          * @deprecated use Roo.getDom instead
18540          * @static
18541          */
18542         getElement: function(id) {
18543             return Roo.getDom(id);
18544         },
18545
18546         /**
18547          * Returns the style property for the DOM element (i.e.,
18548          * document.getElById(id).style)
18549          * @method getCss
18550          * @param {String} id the id of the elment to get
18551          * @return {Object} The style property of the element
18552          * @deprecated use Roo.getDom instead
18553          * @static
18554          */
18555         getCss: function(id) {
18556             var el = Roo.getDom(id);
18557             return (el) ? el.style : null;
18558         },
18559
18560         /**
18561          * Inner class for cached elements
18562          * @class DragDropMgr.ElementWrapper
18563          * @for DragDropMgr
18564          * @private
18565          * @deprecated
18566          */
18567         ElementWrapper: function(el) {
18568                 /**
18569                  * The element
18570                  * @property el
18571                  */
18572                 this.el = el || null;
18573                 /**
18574                  * The element id
18575                  * @property id
18576                  */
18577                 this.id = this.el && el.id;
18578                 /**
18579                  * A reference to the style property
18580                  * @property css
18581                  */
18582                 this.css = this.el && el.style;
18583             },
18584
18585         /**
18586          * Returns the X position of an html element
18587          * @method getPosX
18588          * @param el the element for which to get the position
18589          * @return {int} the X coordinate
18590          * @for DragDropMgr
18591          * @deprecated use Roo.lib.Dom.getX instead
18592          * @static
18593          */
18594         getPosX: function(el) {
18595             return Roo.lib.Dom.getX(el);
18596         },
18597
18598         /**
18599          * Returns the Y position of an html element
18600          * @method getPosY
18601          * @param el the element for which to get the position
18602          * @return {int} the Y coordinate
18603          * @deprecated use Roo.lib.Dom.getY instead
18604          * @static
18605          */
18606         getPosY: function(el) {
18607             return Roo.lib.Dom.getY(el);
18608         },
18609
18610         /**
18611          * Swap two nodes.  In IE, we use the native method, for others we
18612          * emulate the IE behavior
18613          * @method swapNode
18614          * @param n1 the first node to swap
18615          * @param n2 the other node to swap
18616          * @static
18617          */
18618         swapNode: function(n1, n2) {
18619             if (n1.swapNode) {
18620                 n1.swapNode(n2);
18621             } else {
18622                 var p = n2.parentNode;
18623                 var s = n2.nextSibling;
18624
18625                 if (s == n1) {
18626                     p.insertBefore(n1, n2);
18627                 } else if (n2 == n1.nextSibling) {
18628                     p.insertBefore(n2, n1);
18629                 } else {
18630                     n1.parentNode.replaceChild(n2, n1);
18631                     p.insertBefore(n1, s);
18632                 }
18633             }
18634         },
18635
18636         /**
18637          * Returns the current scroll position
18638          * @method getScroll
18639          * @private
18640          * @static
18641          */
18642         getScroll: function () {
18643             var t, l, dde=document.documentElement, db=document.body;
18644             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18645                 t = dde.scrollTop;
18646                 l = dde.scrollLeft;
18647             } else if (db) {
18648                 t = db.scrollTop;
18649                 l = db.scrollLeft;
18650             } else {
18651
18652             }
18653             return { top: t, left: l };
18654         },
18655
18656         /**
18657          * Returns the specified element style property
18658          * @method getStyle
18659          * @param {HTMLElement} el          the element
18660          * @param {string}      styleProp   the style property
18661          * @return {string} The value of the style property
18662          * @deprecated use Roo.lib.Dom.getStyle
18663          * @static
18664          */
18665         getStyle: function(el, styleProp) {
18666             return Roo.fly(el).getStyle(styleProp);
18667         },
18668
18669         /**
18670          * Gets the scrollTop
18671          * @method getScrollTop
18672          * @return {int} the document's scrollTop
18673          * @static
18674          */
18675         getScrollTop: function () { return this.getScroll().top; },
18676
18677         /**
18678          * Gets the scrollLeft
18679          * @method getScrollLeft
18680          * @return {int} the document's scrollTop
18681          * @static
18682          */
18683         getScrollLeft: function () { return this.getScroll().left; },
18684
18685         /**
18686          * Sets the x/y position of an element to the location of the
18687          * target element.
18688          * @method moveToEl
18689          * @param {HTMLElement} moveEl      The element to move
18690          * @param {HTMLElement} targetEl    The position reference element
18691          * @static
18692          */
18693         moveToEl: function (moveEl, targetEl) {
18694             var aCoord = Roo.lib.Dom.getXY(targetEl);
18695             Roo.lib.Dom.setXY(moveEl, aCoord);
18696         },
18697
18698         /**
18699          * Numeric array sort function
18700          * @method numericSort
18701          * @static
18702          */
18703         numericSort: function(a, b) { return (a - b); },
18704
18705         /**
18706          * Internal counter
18707          * @property _timeoutCount
18708          * @private
18709          * @static
18710          */
18711         _timeoutCount: 0,
18712
18713         /**
18714          * Trying to make the load order less important.  Without this we get
18715          * an error if this file is loaded before the Event Utility.
18716          * @method _addListeners
18717          * @private
18718          * @static
18719          */
18720         _addListeners: function() {
18721             var DDM = Roo.dd.DDM;
18722             if ( Roo.lib.Event && document ) {
18723                 DDM._onLoad();
18724             } else {
18725                 if (DDM._timeoutCount > 2000) {
18726                 } else {
18727                     setTimeout(DDM._addListeners, 10);
18728                     if (document && document.body) {
18729                         DDM._timeoutCount += 1;
18730                     }
18731                 }
18732             }
18733         },
18734
18735         /**
18736          * Recursively searches the immediate parent and all child nodes for
18737          * the handle element in order to determine wheter or not it was
18738          * clicked.
18739          * @method handleWasClicked
18740          * @param node the html element to inspect
18741          * @static
18742          */
18743         handleWasClicked: function(node, id) {
18744             if (this.isHandle(id, node.id)) {
18745                 return true;
18746             } else {
18747                 // check to see if this is a text node child of the one we want
18748                 var p = node.parentNode;
18749
18750                 while (p) {
18751                     if (this.isHandle(id, p.id)) {
18752                         return true;
18753                     } else {
18754                         p = p.parentNode;
18755                     }
18756                 }
18757             }
18758
18759             return false;
18760         }
18761
18762     };
18763
18764 }();
18765
18766 // shorter alias, save a few bytes
18767 Roo.dd.DDM = Roo.dd.DragDropMgr;
18768 Roo.dd.DDM._addListeners();
18769
18770 }/*
18771  * Based on:
18772  * Ext JS Library 1.1.1
18773  * Copyright(c) 2006-2007, Ext JS, LLC.
18774  *
18775  * Originally Released Under LGPL - original licence link has changed is not relivant.
18776  *
18777  * Fork - LGPL
18778  * <script type="text/javascript">
18779  */
18780
18781 /**
18782  * @class Roo.dd.DD
18783  * A DragDrop implementation where the linked element follows the
18784  * mouse cursor during a drag.
18785  * @extends Roo.dd.DragDrop
18786  * @constructor
18787  * @param {String} id the id of the linked element
18788  * @param {String} sGroup the group of related DragDrop items
18789  * @param {object} config an object containing configurable attributes
18790  *                Valid properties for DD:
18791  *                    scroll
18792  */
18793 Roo.dd.DD = function(id, sGroup, config) {
18794     if (id) {
18795         this.init(id, sGroup, config);
18796     }
18797 };
18798
18799 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18800
18801     /**
18802      * When set to true, the utility automatically tries to scroll the browser
18803      * window wehn a drag and drop element is dragged near the viewport boundary.
18804      * Defaults to true.
18805      * @property scroll
18806      * @type boolean
18807      */
18808     scroll: true,
18809
18810     /**
18811      * Sets the pointer offset to the distance between the linked element's top
18812      * left corner and the location the element was clicked
18813      * @method autoOffset
18814      * @param {int} iPageX the X coordinate of the click
18815      * @param {int} iPageY the Y coordinate of the click
18816      */
18817     autoOffset: function(iPageX, iPageY) {
18818         var x = iPageX - this.startPageX;
18819         var y = iPageY - this.startPageY;
18820         this.setDelta(x, y);
18821     },
18822
18823     /**
18824      * Sets the pointer offset.  You can call this directly to force the
18825      * offset to be in a particular location (e.g., pass in 0,0 to set it
18826      * to the center of the object)
18827      * @method setDelta
18828      * @param {int} iDeltaX the distance from the left
18829      * @param {int} iDeltaY the distance from the top
18830      */
18831     setDelta: function(iDeltaX, iDeltaY) {
18832         this.deltaX = iDeltaX;
18833         this.deltaY = iDeltaY;
18834     },
18835
18836     /**
18837      * Sets the drag element to the location of the mousedown or click event,
18838      * maintaining the cursor location relative to the location on the element
18839      * that was clicked.  Override this if you want to place the element in a
18840      * location other than where the cursor is.
18841      * @method setDragElPos
18842      * @param {int} iPageX the X coordinate of the mousedown or drag event
18843      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18844      */
18845     setDragElPos: function(iPageX, iPageY) {
18846         // the first time we do this, we are going to check to make sure
18847         // the element has css positioning
18848
18849         var el = this.getDragEl();
18850         this.alignElWithMouse(el, iPageX, iPageY);
18851     },
18852
18853     /**
18854      * Sets the element to the location of the mousedown or click event,
18855      * maintaining the cursor location relative to the location on the element
18856      * that was clicked.  Override this if you want to place the element in a
18857      * location other than where the cursor is.
18858      * @method alignElWithMouse
18859      * @param {HTMLElement} el the element to move
18860      * @param {int} iPageX the X coordinate of the mousedown or drag event
18861      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18862      */
18863     alignElWithMouse: function(el, iPageX, iPageY) {
18864         var oCoord = this.getTargetCoord(iPageX, iPageY);
18865         var fly = el.dom ? el : Roo.fly(el);
18866         if (!this.deltaSetXY) {
18867             var aCoord = [oCoord.x, oCoord.y];
18868             fly.setXY(aCoord);
18869             var newLeft = fly.getLeft(true);
18870             var newTop  = fly.getTop(true);
18871             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18872         } else {
18873             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18874         }
18875
18876         this.cachePosition(oCoord.x, oCoord.y);
18877         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18878         return oCoord;
18879     },
18880
18881     /**
18882      * Saves the most recent position so that we can reset the constraints and
18883      * tick marks on-demand.  We need to know this so that we can calculate the
18884      * number of pixels the element is offset from its original position.
18885      * @method cachePosition
18886      * @param iPageX the current x position (optional, this just makes it so we
18887      * don't have to look it up again)
18888      * @param iPageY the current y position (optional, this just makes it so we
18889      * don't have to look it up again)
18890      */
18891     cachePosition: function(iPageX, iPageY) {
18892         if (iPageX) {
18893             this.lastPageX = iPageX;
18894             this.lastPageY = iPageY;
18895         } else {
18896             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18897             this.lastPageX = aCoord[0];
18898             this.lastPageY = aCoord[1];
18899         }
18900     },
18901
18902     /**
18903      * Auto-scroll the window if the dragged object has been moved beyond the
18904      * visible window boundary.
18905      * @method autoScroll
18906      * @param {int} x the drag element's x position
18907      * @param {int} y the drag element's y position
18908      * @param {int} h the height of the drag element
18909      * @param {int} w the width of the drag element
18910      * @private
18911      */
18912     autoScroll: function(x, y, h, w) {
18913
18914         if (this.scroll) {
18915             // The client height
18916             var clientH = Roo.lib.Dom.getViewWidth();
18917
18918             // The client width
18919             var clientW = Roo.lib.Dom.getViewHeight();
18920
18921             // The amt scrolled down
18922             var st = this.DDM.getScrollTop();
18923
18924             // The amt scrolled right
18925             var sl = this.DDM.getScrollLeft();
18926
18927             // Location of the bottom of the element
18928             var bot = h + y;
18929
18930             // Location of the right of the element
18931             var right = w + x;
18932
18933             // The distance from the cursor to the bottom of the visible area,
18934             // adjusted so that we don't scroll if the cursor is beyond the
18935             // element drag constraints
18936             var toBot = (clientH + st - y - this.deltaY);
18937
18938             // The distance from the cursor to the right of the visible area
18939             var toRight = (clientW + sl - x - this.deltaX);
18940
18941
18942             // How close to the edge the cursor must be before we scroll
18943             // var thresh = (document.all) ? 100 : 40;
18944             var thresh = 40;
18945
18946             // How many pixels to scroll per autoscroll op.  This helps to reduce
18947             // clunky scrolling. IE is more sensitive about this ... it needs this
18948             // value to be higher.
18949             var scrAmt = (document.all) ? 80 : 30;
18950
18951             // Scroll down if we are near the bottom of the visible page and the
18952             // obj extends below the crease
18953             if ( bot > clientH && toBot < thresh ) {
18954                 window.scrollTo(sl, st + scrAmt);
18955             }
18956
18957             // Scroll up if the window is scrolled down and the top of the object
18958             // goes above the top border
18959             if ( y < st && st > 0 && y - st < thresh ) {
18960                 window.scrollTo(sl, st - scrAmt);
18961             }
18962
18963             // Scroll right if the obj is beyond the right border and the cursor is
18964             // near the border.
18965             if ( right > clientW && toRight < thresh ) {
18966                 window.scrollTo(sl + scrAmt, st);
18967             }
18968
18969             // Scroll left if the window has been scrolled to the right and the obj
18970             // extends past the left border
18971             if ( x < sl && sl > 0 && x - sl < thresh ) {
18972                 window.scrollTo(sl - scrAmt, st);
18973             }
18974         }
18975     },
18976
18977     /**
18978      * Finds the location the element should be placed if we want to move
18979      * it to where the mouse location less the click offset would place us.
18980      * @method getTargetCoord
18981      * @param {int} iPageX the X coordinate of the click
18982      * @param {int} iPageY the Y coordinate of the click
18983      * @return an object that contains the coordinates (Object.x and Object.y)
18984      * @private
18985      */
18986     getTargetCoord: function(iPageX, iPageY) {
18987
18988
18989         var x = iPageX - this.deltaX;
18990         var y = iPageY - this.deltaY;
18991
18992         if (this.constrainX) {
18993             if (x < this.minX) { x = this.minX; }
18994             if (x > this.maxX) { x = this.maxX; }
18995         }
18996
18997         if (this.constrainY) {
18998             if (y < this.minY) { y = this.minY; }
18999             if (y > this.maxY) { y = this.maxY; }
19000         }
19001
19002         x = this.getTick(x, this.xTicks);
19003         y = this.getTick(y, this.yTicks);
19004
19005
19006         return {x:x, y:y};
19007     },
19008
19009     /*
19010      * Sets up config options specific to this class. Overrides
19011      * Roo.dd.DragDrop, but all versions of this method through the
19012      * inheritance chain are called
19013      */
19014     applyConfig: function() {
19015         Roo.dd.DD.superclass.applyConfig.call(this);
19016         this.scroll = (this.config.scroll !== false);
19017     },
19018
19019     /*
19020      * Event that fires prior to the onMouseDown event.  Overrides
19021      * Roo.dd.DragDrop.
19022      */
19023     b4MouseDown: function(e) {
19024         // this.resetConstraints();
19025         this.autoOffset(e.getPageX(),
19026                             e.getPageY());
19027     },
19028
19029     /*
19030      * Event that fires prior to the onDrag event.  Overrides
19031      * Roo.dd.DragDrop.
19032      */
19033     b4Drag: function(e) {
19034         this.setDragElPos(e.getPageX(),
19035                             e.getPageY());
19036     },
19037
19038     toString: function() {
19039         return ("DD " + this.id);
19040     }
19041
19042     //////////////////////////////////////////////////////////////////////////
19043     // Debugging ygDragDrop events that can be overridden
19044     //////////////////////////////////////////////////////////////////////////
19045     /*
19046     startDrag: function(x, y) {
19047     },
19048
19049     onDrag: function(e) {
19050     },
19051
19052     onDragEnter: function(e, id) {
19053     },
19054
19055     onDragOver: function(e, id) {
19056     },
19057
19058     onDragOut: function(e, id) {
19059     },
19060
19061     onDragDrop: function(e, id) {
19062     },
19063
19064     endDrag: function(e) {
19065     }
19066
19067     */
19068
19069 });/*
19070  * Based on:
19071  * Ext JS Library 1.1.1
19072  * Copyright(c) 2006-2007, Ext JS, LLC.
19073  *
19074  * Originally Released Under LGPL - original licence link has changed is not relivant.
19075  *
19076  * Fork - LGPL
19077  * <script type="text/javascript">
19078  */
19079
19080 /**
19081  * @class Roo.dd.DDProxy
19082  * A DragDrop implementation that inserts an empty, bordered div into
19083  * the document that follows the cursor during drag operations.  At the time of
19084  * the click, the frame div is resized to the dimensions of the linked html
19085  * element, and moved to the exact location of the linked element.
19086  *
19087  * References to the "frame" element refer to the single proxy element that
19088  * was created to be dragged in place of all DDProxy elements on the
19089  * page.
19090  *
19091  * @extends Roo.dd.DD
19092  * @constructor
19093  * @param {String} id the id of the linked html element
19094  * @param {String} sGroup the group of related DragDrop objects
19095  * @param {object} config an object containing configurable attributes
19096  *                Valid properties for DDProxy in addition to those in DragDrop:
19097  *                   resizeFrame, centerFrame, dragElId
19098  */
19099 Roo.dd.DDProxy = function(id, sGroup, config) {
19100     if (id) {
19101         this.init(id, sGroup, config);
19102         this.initFrame();
19103     }
19104 };
19105
19106 /**
19107  * The default drag frame div id
19108  * @property Roo.dd.DDProxy.dragElId
19109  * @type String
19110  * @static
19111  */
19112 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19113
19114 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19115
19116     /**
19117      * By default we resize the drag frame to be the same size as the element
19118      * we want to drag (this is to get the frame effect).  We can turn it off
19119      * if we want a different behavior.
19120      * @property resizeFrame
19121      * @type boolean
19122      */
19123     resizeFrame: true,
19124
19125     /**
19126      * By default the frame is positioned exactly where the drag element is, so
19127      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19128      * you do not have constraints on the obj is to have the drag frame centered
19129      * around the cursor.  Set centerFrame to true for this effect.
19130      * @property centerFrame
19131      * @type boolean
19132      */
19133     centerFrame: false,
19134
19135     /**
19136      * Creates the proxy element if it does not yet exist
19137      * @method createFrame
19138      */
19139     createFrame: function() {
19140         var self = this;
19141         var body = document.body;
19142
19143         if (!body || !body.firstChild) {
19144             setTimeout( function() { self.createFrame(); }, 50 );
19145             return;
19146         }
19147
19148         var div = this.getDragEl();
19149
19150         if (!div) {
19151             div    = document.createElement("div");
19152             div.id = this.dragElId;
19153             var s  = div.style;
19154
19155             s.position   = "absolute";
19156             s.visibility = "hidden";
19157             s.cursor     = "move";
19158             s.border     = "2px solid #aaa";
19159             s.zIndex     = 999;
19160
19161             // appendChild can blow up IE if invoked prior to the window load event
19162             // while rendering a table.  It is possible there are other scenarios
19163             // that would cause this to happen as well.
19164             body.insertBefore(div, body.firstChild);
19165         }
19166     },
19167
19168     /**
19169      * Initialization for the drag frame element.  Must be called in the
19170      * constructor of all subclasses
19171      * @method initFrame
19172      */
19173     initFrame: function() {
19174         this.createFrame();
19175     },
19176
19177     applyConfig: function() {
19178         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19179
19180         this.resizeFrame = (this.config.resizeFrame !== false);
19181         this.centerFrame = (this.config.centerFrame);
19182         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19183     },
19184
19185     /**
19186      * Resizes the drag frame to the dimensions of the clicked object, positions
19187      * it over the object, and finally displays it
19188      * @method showFrame
19189      * @param {int} iPageX X click position
19190      * @param {int} iPageY Y click position
19191      * @private
19192      */
19193     showFrame: function(iPageX, iPageY) {
19194         var el = this.getEl();
19195         var dragEl = this.getDragEl();
19196         var s = dragEl.style;
19197
19198         this._resizeProxy();
19199
19200         if (this.centerFrame) {
19201             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19202                            Math.round(parseInt(s.height, 10)/2) );
19203         }
19204
19205         this.setDragElPos(iPageX, iPageY);
19206
19207         Roo.fly(dragEl).show();
19208     },
19209
19210     /**
19211      * The proxy is automatically resized to the dimensions of the linked
19212      * element when a drag is initiated, unless resizeFrame is set to false
19213      * @method _resizeProxy
19214      * @private
19215      */
19216     _resizeProxy: function() {
19217         if (this.resizeFrame) {
19218             var el = this.getEl();
19219             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19220         }
19221     },
19222
19223     // overrides Roo.dd.DragDrop
19224     b4MouseDown: function(e) {
19225         var x = e.getPageX();
19226         var y = e.getPageY();
19227         this.autoOffset(x, y);
19228         this.setDragElPos(x, y);
19229     },
19230
19231     // overrides Roo.dd.DragDrop
19232     b4StartDrag: function(x, y) {
19233         // show the drag frame
19234         this.showFrame(x, y);
19235     },
19236
19237     // overrides Roo.dd.DragDrop
19238     b4EndDrag: function(e) {
19239         Roo.fly(this.getDragEl()).hide();
19240     },
19241
19242     // overrides Roo.dd.DragDrop
19243     // By default we try to move the element to the last location of the frame.
19244     // This is so that the default behavior mirrors that of Roo.dd.DD.
19245     endDrag: function(e) {
19246
19247         var lel = this.getEl();
19248         var del = this.getDragEl();
19249
19250         // Show the drag frame briefly so we can get its position
19251         del.style.visibility = "";
19252
19253         this.beforeMove();
19254         // Hide the linked element before the move to get around a Safari
19255         // rendering bug.
19256         lel.style.visibility = "hidden";
19257         Roo.dd.DDM.moveToEl(lel, del);
19258         del.style.visibility = "hidden";
19259         lel.style.visibility = "";
19260
19261         this.afterDrag();
19262     },
19263
19264     beforeMove : function(){
19265
19266     },
19267
19268     afterDrag : function(){
19269
19270     },
19271
19272     toString: function() {
19273         return ("DDProxy " + this.id);
19274     }
19275
19276 });
19277 /*
19278  * Based on:
19279  * Ext JS Library 1.1.1
19280  * Copyright(c) 2006-2007, Ext JS, LLC.
19281  *
19282  * Originally Released Under LGPL - original licence link has changed is not relivant.
19283  *
19284  * Fork - LGPL
19285  * <script type="text/javascript">
19286  */
19287
19288  /**
19289  * @class Roo.dd.DDTarget
19290  * A DragDrop implementation that does not move, but can be a drop
19291  * target.  You would get the same result by simply omitting implementation
19292  * for the event callbacks, but this way we reduce the processing cost of the
19293  * event listener and the callbacks.
19294  * @extends Roo.dd.DragDrop
19295  * @constructor
19296  * @param {String} id the id of the element that is a drop target
19297  * @param {String} sGroup the group of related DragDrop objects
19298  * @param {object} config an object containing configurable attributes
19299  *                 Valid properties for DDTarget in addition to those in
19300  *                 DragDrop:
19301  *                    none
19302  */
19303 Roo.dd.DDTarget = function(id, sGroup, config) {
19304     if (id) {
19305         this.initTarget(id, sGroup, config);
19306     }
19307     if (config.listeners || config.events) { 
19308        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19309             listeners : config.listeners || {}, 
19310             events : config.events || {} 
19311         });    
19312     }
19313 };
19314
19315 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19316 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19317     toString: function() {
19318         return ("DDTarget " + this.id);
19319     }
19320 });
19321 /*
19322  * Based on:
19323  * Ext JS Library 1.1.1
19324  * Copyright(c) 2006-2007, Ext JS, LLC.
19325  *
19326  * Originally Released Under LGPL - original licence link has changed is not relivant.
19327  *
19328  * Fork - LGPL
19329  * <script type="text/javascript">
19330  */
19331  
19332
19333 /**
19334  * @class Roo.dd.ScrollManager
19335  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19336  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19337  * @singleton
19338  */
19339 Roo.dd.ScrollManager = function(){
19340     var ddm = Roo.dd.DragDropMgr;
19341     var els = {};
19342     var dragEl = null;
19343     var proc = {};
19344     
19345     
19346     
19347     var onStop = function(e){
19348         dragEl = null;
19349         clearProc();
19350     };
19351     
19352     var triggerRefresh = function(){
19353         if(ddm.dragCurrent){
19354              ddm.refreshCache(ddm.dragCurrent.groups);
19355         }
19356     };
19357     
19358     var doScroll = function(){
19359         if(ddm.dragCurrent){
19360             var dds = Roo.dd.ScrollManager;
19361             if(!dds.animate){
19362                 if(proc.el.scroll(proc.dir, dds.increment)){
19363                     triggerRefresh();
19364                 }
19365             }else{
19366                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19367             }
19368         }
19369     };
19370     
19371     var clearProc = function(){
19372         if(proc.id){
19373             clearInterval(proc.id);
19374         }
19375         proc.id = 0;
19376         proc.el = null;
19377         proc.dir = "";
19378     };
19379     
19380     var startProc = function(el, dir){
19381          Roo.log('scroll startproc');
19382         clearProc();
19383         proc.el = el;
19384         proc.dir = dir;
19385         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19386     };
19387     
19388     var onFire = function(e, isDrop){
19389        
19390         if(isDrop || !ddm.dragCurrent){ return; }
19391         var dds = Roo.dd.ScrollManager;
19392         if(!dragEl || dragEl != ddm.dragCurrent){
19393             dragEl = ddm.dragCurrent;
19394             // refresh regions on drag start
19395             dds.refreshCache();
19396         }
19397         
19398         var xy = Roo.lib.Event.getXY(e);
19399         var pt = new Roo.lib.Point(xy[0], xy[1]);
19400         for(var id in els){
19401             var el = els[id], r = el._region;
19402             if(r && r.contains(pt) && el.isScrollable()){
19403                 if(r.bottom - pt.y <= dds.thresh){
19404                     if(proc.el != el){
19405                         startProc(el, "down");
19406                     }
19407                     return;
19408                 }else if(r.right - pt.x <= dds.thresh){
19409                     if(proc.el != el){
19410                         startProc(el, "left");
19411                     }
19412                     return;
19413                 }else if(pt.y - r.top <= dds.thresh){
19414                     if(proc.el != el){
19415                         startProc(el, "up");
19416                     }
19417                     return;
19418                 }else if(pt.x - r.left <= dds.thresh){
19419                     if(proc.el != el){
19420                         startProc(el, "right");
19421                     }
19422                     return;
19423                 }
19424             }
19425         }
19426         clearProc();
19427     };
19428     
19429     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19430     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19431     
19432     return {
19433         /**
19434          * Registers new overflow element(s) to auto scroll
19435          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19436          */
19437         register : function(el){
19438             if(el instanceof Array){
19439                 for(var i = 0, len = el.length; i < len; i++) {
19440                         this.register(el[i]);
19441                 }
19442             }else{
19443                 el = Roo.get(el);
19444                 els[el.id] = el;
19445             }
19446             Roo.dd.ScrollManager.els = els;
19447         },
19448         
19449         /**
19450          * Unregisters overflow element(s) so they are no longer scrolled
19451          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19452          */
19453         unregister : function(el){
19454             if(el instanceof Array){
19455                 for(var i = 0, len = el.length; i < len; i++) {
19456                         this.unregister(el[i]);
19457                 }
19458             }else{
19459                 el = Roo.get(el);
19460                 delete els[el.id];
19461             }
19462         },
19463         
19464         /**
19465          * The number of pixels from the edge of a container the pointer needs to be to 
19466          * trigger scrolling (defaults to 25)
19467          * @type Number
19468          */
19469         thresh : 25,
19470         
19471         /**
19472          * The number of pixels to scroll in each scroll increment (defaults to 50)
19473          * @type Number
19474          */
19475         increment : 100,
19476         
19477         /**
19478          * The frequency of scrolls in milliseconds (defaults to 500)
19479          * @type Number
19480          */
19481         frequency : 500,
19482         
19483         /**
19484          * True to animate the scroll (defaults to true)
19485          * @type Boolean
19486          */
19487         animate: true,
19488         
19489         /**
19490          * The animation duration in seconds - 
19491          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19492          * @type Number
19493          */
19494         animDuration: .4,
19495         
19496         /**
19497          * Manually trigger a cache refresh.
19498          */
19499         refreshCache : function(){
19500             for(var id in els){
19501                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19502                     els[id]._region = els[id].getRegion();
19503                 }
19504             }
19505         }
19506     };
19507 }();/*
19508  * Based on:
19509  * Ext JS Library 1.1.1
19510  * Copyright(c) 2006-2007, Ext JS, LLC.
19511  *
19512  * Originally Released Under LGPL - original licence link has changed is not relivant.
19513  *
19514  * Fork - LGPL
19515  * <script type="text/javascript">
19516  */
19517  
19518
19519 /**
19520  * @class Roo.dd.Registry
19521  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19522  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19523  * @singleton
19524  */
19525 Roo.dd.Registry = function(){
19526     var elements = {}; 
19527     var handles = {}; 
19528     var autoIdSeed = 0;
19529
19530     var getId = function(el, autogen){
19531         if(typeof el == "string"){
19532             return el;
19533         }
19534         var id = el.id;
19535         if(!id && autogen !== false){
19536             id = "roodd-" + (++autoIdSeed);
19537             el.id = id;
19538         }
19539         return id;
19540     };
19541     
19542     return {
19543     /**
19544      * Register a drag drop element
19545      * @param {String|HTMLElement} element The id or DOM node to register
19546      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19547      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19548      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19549      * populated in the data object (if applicable):
19550      * <pre>
19551 Value      Description<br />
19552 ---------  ------------------------------------------<br />
19553 handles    Array of DOM nodes that trigger dragging<br />
19554            for the element being registered<br />
19555 isHandle   True if the element passed in triggers<br />
19556            dragging itself, else false
19557 </pre>
19558      */
19559         register : function(el, data){
19560             data = data || {};
19561             if(typeof el == "string"){
19562                 el = document.getElementById(el);
19563             }
19564             data.ddel = el;
19565             elements[getId(el)] = data;
19566             if(data.isHandle !== false){
19567                 handles[data.ddel.id] = data;
19568             }
19569             if(data.handles){
19570                 var hs = data.handles;
19571                 for(var i = 0, len = hs.length; i < len; i++){
19572                         handles[getId(hs[i])] = data;
19573                 }
19574             }
19575         },
19576
19577     /**
19578      * Unregister a drag drop element
19579      * @param {String|HTMLElement}  element The id or DOM node to unregister
19580      */
19581         unregister : function(el){
19582             var id = getId(el, false);
19583             var data = elements[id];
19584             if(data){
19585                 delete elements[id];
19586                 if(data.handles){
19587                     var hs = data.handles;
19588                     for(var i = 0, len = hs.length; i < len; i++){
19589                         delete handles[getId(hs[i], false)];
19590                     }
19591                 }
19592             }
19593         },
19594
19595     /**
19596      * Returns the handle registered for a DOM Node by id
19597      * @param {String|HTMLElement} id The DOM node or id to look up
19598      * @return {Object} handle The custom handle data
19599      */
19600         getHandle : function(id){
19601             if(typeof id != "string"){ // must be element?
19602                 id = id.id;
19603             }
19604             return handles[id];
19605         },
19606
19607     /**
19608      * Returns the handle that is registered for the DOM node that is the target of the event
19609      * @param {Event} e The event
19610      * @return {Object} handle The custom handle data
19611      */
19612         getHandleFromEvent : function(e){
19613             var t = Roo.lib.Event.getTarget(e);
19614             return t ? handles[t.id] : null;
19615         },
19616
19617     /**
19618      * Returns a custom data object that is registered for a DOM node by id
19619      * @param {String|HTMLElement} id The DOM node or id to look up
19620      * @return {Object} data The custom data
19621      */
19622         getTarget : function(id){
19623             if(typeof id != "string"){ // must be element?
19624                 id = id.id;
19625             }
19626             return elements[id];
19627         },
19628
19629     /**
19630      * Returns a custom data object that is registered for the DOM node that is the target of the event
19631      * @param {Event} e The event
19632      * @return {Object} data The custom data
19633      */
19634         getTargetFromEvent : function(e){
19635             var t = Roo.lib.Event.getTarget(e);
19636             return t ? elements[t.id] || handles[t.id] : null;
19637         }
19638     };
19639 }();/*
19640  * Based on:
19641  * Ext JS Library 1.1.1
19642  * Copyright(c) 2006-2007, Ext JS, LLC.
19643  *
19644  * Originally Released Under LGPL - original licence link has changed is not relivant.
19645  *
19646  * Fork - LGPL
19647  * <script type="text/javascript">
19648  */
19649  
19650
19651 /**
19652  * @class Roo.dd.StatusProxy
19653  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19654  * default drag proxy used by all Roo.dd components.
19655  * @constructor
19656  * @param {Object} config
19657  */
19658 Roo.dd.StatusProxy = function(config){
19659     Roo.apply(this, config);
19660     this.id = this.id || Roo.id();
19661     this.el = new Roo.Layer({
19662         dh: {
19663             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19664                 {tag: "div", cls: "x-dd-drop-icon"},
19665                 {tag: "div", cls: "x-dd-drag-ghost"}
19666             ]
19667         }, 
19668         shadow: !config || config.shadow !== false
19669     });
19670     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19671     this.dropStatus = this.dropNotAllowed;
19672 };
19673
19674 Roo.dd.StatusProxy.prototype = {
19675     /**
19676      * @cfg {String} dropAllowed
19677      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19678      */
19679     dropAllowed : "x-dd-drop-ok",
19680     /**
19681      * @cfg {String} dropNotAllowed
19682      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19683      */
19684     dropNotAllowed : "x-dd-drop-nodrop",
19685
19686     /**
19687      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19688      * over the current target element.
19689      * @param {String} cssClass The css class for the new drop status indicator image
19690      */
19691     setStatus : function(cssClass){
19692         cssClass = cssClass || this.dropNotAllowed;
19693         if(this.dropStatus != cssClass){
19694             this.el.replaceClass(this.dropStatus, cssClass);
19695             this.dropStatus = cssClass;
19696         }
19697     },
19698
19699     /**
19700      * Resets the status indicator to the default dropNotAllowed value
19701      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19702      */
19703     reset : function(clearGhost){
19704         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19705         this.dropStatus = this.dropNotAllowed;
19706         if(clearGhost){
19707             this.ghost.update("");
19708         }
19709     },
19710
19711     /**
19712      * Updates the contents of the ghost element
19713      * @param {String} html The html that will replace the current innerHTML of the ghost element
19714      */
19715     update : function(html){
19716         if(typeof html == "string"){
19717             this.ghost.update(html);
19718         }else{
19719             this.ghost.update("");
19720             html.style.margin = "0";
19721             this.ghost.dom.appendChild(html);
19722         }
19723         // ensure float = none set?? cant remember why though.
19724         var el = this.ghost.dom.firstChild;
19725                 if(el){
19726                         Roo.fly(el).setStyle('float', 'none');
19727                 }
19728     },
19729     
19730     /**
19731      * Returns the underlying proxy {@link Roo.Layer}
19732      * @return {Roo.Layer} el
19733     */
19734     getEl : function(){
19735         return this.el;
19736     },
19737
19738     /**
19739      * Returns the ghost element
19740      * @return {Roo.Element} el
19741      */
19742     getGhost : function(){
19743         return this.ghost;
19744     },
19745
19746     /**
19747      * Hides the proxy
19748      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19749      */
19750     hide : function(clear){
19751         this.el.hide();
19752         if(clear){
19753             this.reset(true);
19754         }
19755     },
19756
19757     /**
19758      * Stops the repair animation if it's currently running
19759      */
19760     stop : function(){
19761         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19762             this.anim.stop();
19763         }
19764     },
19765
19766     /**
19767      * Displays this proxy
19768      */
19769     show : function(){
19770         this.el.show();
19771     },
19772
19773     /**
19774      * Force the Layer to sync its shadow and shim positions to the element
19775      */
19776     sync : function(){
19777         this.el.sync();
19778     },
19779
19780     /**
19781      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19782      * invalid drop operation by the item being dragged.
19783      * @param {Array} xy The XY position of the element ([x, y])
19784      * @param {Function} callback The function to call after the repair is complete
19785      * @param {Object} scope The scope in which to execute the callback
19786      */
19787     repair : function(xy, callback, scope){
19788         this.callback = callback;
19789         this.scope = scope;
19790         if(xy && this.animRepair !== false){
19791             this.el.addClass("x-dd-drag-repair");
19792             this.el.hideUnders(true);
19793             this.anim = this.el.shift({
19794                 duration: this.repairDuration || .5,
19795                 easing: 'easeOut',
19796                 xy: xy,
19797                 stopFx: true,
19798                 callback: this.afterRepair,
19799                 scope: this
19800             });
19801         }else{
19802             this.afterRepair();
19803         }
19804     },
19805
19806     // private
19807     afterRepair : function(){
19808         this.hide(true);
19809         if(typeof this.callback == "function"){
19810             this.callback.call(this.scope || this);
19811         }
19812         this.callback = null;
19813         this.scope = null;
19814     }
19815 };/*
19816  * Based on:
19817  * Ext JS Library 1.1.1
19818  * Copyright(c) 2006-2007, Ext JS, LLC.
19819  *
19820  * Originally Released Under LGPL - original licence link has changed is not relivant.
19821  *
19822  * Fork - LGPL
19823  * <script type="text/javascript">
19824  */
19825
19826 /**
19827  * @class Roo.dd.DragSource
19828  * @extends Roo.dd.DDProxy
19829  * A simple class that provides the basic implementation needed to make any element draggable.
19830  * @constructor
19831  * @param {String/HTMLElement/Element} el The container element
19832  * @param {Object} config
19833  */
19834 Roo.dd.DragSource = function(el, config){
19835     this.el = Roo.get(el);
19836     this.dragData = {};
19837     
19838     Roo.apply(this, config);
19839     
19840     if(!this.proxy){
19841         this.proxy = new Roo.dd.StatusProxy();
19842     }
19843
19844     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19845           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19846     
19847     this.dragging = false;
19848 };
19849
19850 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19851     /**
19852      * @cfg {String} dropAllowed
19853      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19854      */
19855     dropAllowed : "x-dd-drop-ok",
19856     /**
19857      * @cfg {String} dropNotAllowed
19858      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19859      */
19860     dropNotAllowed : "x-dd-drop-nodrop",
19861
19862     /**
19863      * Returns the data object associated with this drag source
19864      * @return {Object} data An object containing arbitrary data
19865      */
19866     getDragData : function(e){
19867         return this.dragData;
19868     },
19869
19870     // private
19871     onDragEnter : function(e, id){
19872         var target = Roo.dd.DragDropMgr.getDDById(id);
19873         this.cachedTarget = target;
19874         if(this.beforeDragEnter(target, e, id) !== false){
19875             if(target.isNotifyTarget){
19876                 var status = target.notifyEnter(this, e, this.dragData);
19877                 this.proxy.setStatus(status);
19878             }else{
19879                 this.proxy.setStatus(this.dropAllowed);
19880             }
19881             
19882             if(this.afterDragEnter){
19883                 /**
19884                  * An empty function by default, but provided so that you can perform a custom action
19885                  * when the dragged item enters the drop target by providing an implementation.
19886                  * @param {Roo.dd.DragDrop} target The drop target
19887                  * @param {Event} e The event object
19888                  * @param {String} id The id of the dragged element
19889                  * @method afterDragEnter
19890                  */
19891                 this.afterDragEnter(target, e, id);
19892             }
19893         }
19894     },
19895
19896     /**
19897      * An empty function by default, but provided so that you can perform a custom action
19898      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19899      * @param {Roo.dd.DragDrop} target The drop target
19900      * @param {Event} e The event object
19901      * @param {String} id The id of the dragged element
19902      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19903      */
19904     beforeDragEnter : function(target, e, id){
19905         return true;
19906     },
19907
19908     // private
19909     alignElWithMouse: function() {
19910         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19911         this.proxy.sync();
19912     },
19913
19914     // private
19915     onDragOver : function(e, id){
19916         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19917         if(this.beforeDragOver(target, e, id) !== false){
19918             if(target.isNotifyTarget){
19919                 var status = target.notifyOver(this, e, this.dragData);
19920                 this.proxy.setStatus(status);
19921             }
19922
19923             if(this.afterDragOver){
19924                 /**
19925                  * An empty function by default, but provided so that you can perform a custom action
19926                  * while the dragged item is over the drop target by providing an implementation.
19927                  * @param {Roo.dd.DragDrop} target The drop target
19928                  * @param {Event} e The event object
19929                  * @param {String} id The id of the dragged element
19930                  * @method afterDragOver
19931                  */
19932                 this.afterDragOver(target, e, id);
19933             }
19934         }
19935     },
19936
19937     /**
19938      * An empty function by default, but provided so that you can perform a custom action
19939      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19940      * @param {Roo.dd.DragDrop} target The drop target
19941      * @param {Event} e The event object
19942      * @param {String} id The id of the dragged element
19943      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19944      */
19945     beforeDragOver : function(target, e, id){
19946         return true;
19947     },
19948
19949     // private
19950     onDragOut : function(e, id){
19951         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19952         if(this.beforeDragOut(target, e, id) !== false){
19953             if(target.isNotifyTarget){
19954                 target.notifyOut(this, e, this.dragData);
19955             }
19956             this.proxy.reset();
19957             if(this.afterDragOut){
19958                 /**
19959                  * An empty function by default, but provided so that you can perform a custom action
19960                  * after the dragged item is dragged out of the target without dropping.
19961                  * @param {Roo.dd.DragDrop} target The drop target
19962                  * @param {Event} e The event object
19963                  * @param {String} id The id of the dragged element
19964                  * @method afterDragOut
19965                  */
19966                 this.afterDragOut(target, e, id);
19967             }
19968         }
19969         this.cachedTarget = null;
19970     },
19971
19972     /**
19973      * An empty function by default, but provided so that you can perform a custom action before the dragged
19974      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
19975      * @param {Roo.dd.DragDrop} target The drop target
19976      * @param {Event} e The event object
19977      * @param {String} id The id of the dragged element
19978      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19979      */
19980     beforeDragOut : function(target, e, id){
19981         return true;
19982     },
19983     
19984     // private
19985     onDragDrop : function(e, id){
19986         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19987         if(this.beforeDragDrop(target, e, id) !== false){
19988             if(target.isNotifyTarget){
19989                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
19990                     this.onValidDrop(target, e, id);
19991                 }else{
19992                     this.onInvalidDrop(target, e, id);
19993                 }
19994             }else{
19995                 this.onValidDrop(target, e, id);
19996             }
19997             
19998             if(this.afterDragDrop){
19999                 /**
20000                  * An empty function by default, but provided so that you can perform a custom action
20001                  * after a valid drag drop has occurred by providing an implementation.
20002                  * @param {Roo.dd.DragDrop} target The drop target
20003                  * @param {Event} e The event object
20004                  * @param {String} id The id of the dropped element
20005                  * @method afterDragDrop
20006                  */
20007                 this.afterDragDrop(target, e, id);
20008             }
20009         }
20010         delete this.cachedTarget;
20011     },
20012
20013     /**
20014      * An empty function by default, but provided so that you can perform a custom action before the dragged
20015      * item is dropped onto the target and optionally cancel the onDragDrop.
20016      * @param {Roo.dd.DragDrop} target The drop target
20017      * @param {Event} e The event object
20018      * @param {String} id The id of the dragged element
20019      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20020      */
20021     beforeDragDrop : function(target, e, id){
20022         return true;
20023     },
20024
20025     // private
20026     onValidDrop : function(target, e, id){
20027         this.hideProxy();
20028         if(this.afterValidDrop){
20029             /**
20030              * An empty function by default, but provided so that you can perform a custom action
20031              * after a valid drop has occurred by providing an implementation.
20032              * @param {Object} target The target DD 
20033              * @param {Event} e The event object
20034              * @param {String} id The id of the dropped element
20035              * @method afterInvalidDrop
20036              */
20037             this.afterValidDrop(target, e, id);
20038         }
20039     },
20040
20041     // private
20042     getRepairXY : function(e, data){
20043         return this.el.getXY();  
20044     },
20045
20046     // private
20047     onInvalidDrop : function(target, e, id){
20048         this.beforeInvalidDrop(target, e, id);
20049         if(this.cachedTarget){
20050             if(this.cachedTarget.isNotifyTarget){
20051                 this.cachedTarget.notifyOut(this, e, this.dragData);
20052             }
20053             this.cacheTarget = null;
20054         }
20055         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20056
20057         if(this.afterInvalidDrop){
20058             /**
20059              * An empty function by default, but provided so that you can perform a custom action
20060              * after an invalid drop has occurred by providing an implementation.
20061              * @param {Event} e The event object
20062              * @param {String} id The id of the dropped element
20063              * @method afterInvalidDrop
20064              */
20065             this.afterInvalidDrop(e, id);
20066         }
20067     },
20068
20069     // private
20070     afterRepair : function(){
20071         if(Roo.enableFx){
20072             this.el.highlight(this.hlColor || "c3daf9");
20073         }
20074         this.dragging = false;
20075     },
20076
20077     /**
20078      * An empty function by default, but provided so that you can perform a custom action after an invalid
20079      * drop has occurred.
20080      * @param {Roo.dd.DragDrop} target The drop target
20081      * @param {Event} e The event object
20082      * @param {String} id The id of the dragged element
20083      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20084      */
20085     beforeInvalidDrop : function(target, e, id){
20086         return true;
20087     },
20088
20089     // private
20090     handleMouseDown : function(e){
20091         if(this.dragging) {
20092             return;
20093         }
20094         var data = this.getDragData(e);
20095         if(data && this.onBeforeDrag(data, e) !== false){
20096             this.dragData = data;
20097             this.proxy.stop();
20098             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20099         } 
20100     },
20101
20102     /**
20103      * An empty function by default, but provided so that you can perform a custom action before the initial
20104      * drag event begins and optionally cancel it.
20105      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20106      * @param {Event} e The event object
20107      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20108      */
20109     onBeforeDrag : function(data, e){
20110         return true;
20111     },
20112
20113     /**
20114      * An empty function by default, but provided so that you can perform a custom action once the initial
20115      * drag event has begun.  The drag cannot be canceled from this function.
20116      * @param {Number} x The x position of the click on the dragged object
20117      * @param {Number} y The y position of the click on the dragged object
20118      */
20119     onStartDrag : Roo.emptyFn,
20120
20121     // private - YUI override
20122     startDrag : function(x, y){
20123         this.proxy.reset();
20124         this.dragging = true;
20125         this.proxy.update("");
20126         this.onInitDrag(x, y);
20127         this.proxy.show();
20128     },
20129
20130     // private
20131     onInitDrag : function(x, y){
20132         var clone = this.el.dom.cloneNode(true);
20133         clone.id = Roo.id(); // prevent duplicate ids
20134         this.proxy.update(clone);
20135         this.onStartDrag(x, y);
20136         return true;
20137     },
20138
20139     /**
20140      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20141      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20142      */
20143     getProxy : function(){
20144         return this.proxy;  
20145     },
20146
20147     /**
20148      * Hides the drag source's {@link Roo.dd.StatusProxy}
20149      */
20150     hideProxy : function(){
20151         this.proxy.hide();  
20152         this.proxy.reset(true);
20153         this.dragging = false;
20154     },
20155
20156     // private
20157     triggerCacheRefresh : function(){
20158         Roo.dd.DDM.refreshCache(this.groups);
20159     },
20160
20161     // private - override to prevent hiding
20162     b4EndDrag: function(e) {
20163     },
20164
20165     // private - override to prevent moving
20166     endDrag : function(e){
20167         this.onEndDrag(this.dragData, e);
20168     },
20169
20170     // private
20171     onEndDrag : function(data, e){
20172     },
20173     
20174     // private - pin to cursor
20175     autoOffset : function(x, y) {
20176         this.setDelta(-12, -20);
20177     }    
20178 });/*
20179  * Based on:
20180  * Ext JS Library 1.1.1
20181  * Copyright(c) 2006-2007, Ext JS, LLC.
20182  *
20183  * Originally Released Under LGPL - original licence link has changed is not relivant.
20184  *
20185  * Fork - LGPL
20186  * <script type="text/javascript">
20187  */
20188
20189
20190 /**
20191  * @class Roo.dd.DropTarget
20192  * @extends Roo.dd.DDTarget
20193  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20194  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20195  * @constructor
20196  * @param {String/HTMLElement/Element} el The container element
20197  * @param {Object} config
20198  */
20199 Roo.dd.DropTarget = function(el, config){
20200     this.el = Roo.get(el);
20201     
20202     var listeners = false; ;
20203     if (config && config.listeners) {
20204         listeners= config.listeners;
20205         delete config.listeners;
20206     }
20207     Roo.apply(this, config);
20208     
20209     if(this.containerScroll){
20210         Roo.dd.ScrollManager.register(this.el);
20211     }
20212     this.addEvents( {
20213          /**
20214          * @scope Roo.dd.DropTarget
20215          */
20216          
20217          /**
20218          * @event enter
20219          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20220          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20221          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20222          * 
20223          * IMPORTANT : it should set this.overClass and this.dropAllowed
20224          * 
20225          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20226          * @param {Event} e The event
20227          * @param {Object} data An object containing arbitrary data supplied by the drag source
20228          */
20229         "enter" : true,
20230         
20231          /**
20232          * @event over
20233          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20234          * This method will be called on every mouse movement while the drag source is over the drop target.
20235          * This default implementation simply returns the dropAllowed config value.
20236          * 
20237          * IMPORTANT : it should set this.dropAllowed
20238          * 
20239          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20240          * @param {Event} e The event
20241          * @param {Object} data An object containing arbitrary data supplied by the drag source
20242          
20243          */
20244         "over" : true,
20245         /**
20246          * @event out
20247          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20248          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20249          * overClass (if any) from the drop element.
20250          * 
20251          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20252          * @param {Event} e The event
20253          * @param {Object} data An object containing arbitrary data supplied by the drag source
20254          */
20255          "out" : true,
20256          
20257         /**
20258          * @event drop
20259          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20260          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20261          * implementation that does something to process the drop event and returns true so that the drag source's
20262          * repair action does not run.
20263          * 
20264          * IMPORTANT : it should set this.success
20265          * 
20266          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20267          * @param {Event} e The event
20268          * @param {Object} data An object containing arbitrary data supplied by the drag source
20269         */
20270          "drop" : true
20271     });
20272             
20273      
20274     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20275         this.el.dom, 
20276         this.ddGroup || this.group,
20277         {
20278             isTarget: true,
20279             listeners : listeners || {} 
20280            
20281         
20282         }
20283     );
20284
20285 };
20286
20287 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20288     /**
20289      * @cfg {String} overClass
20290      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20291      */
20292      /**
20293      * @cfg {String} ddGroup
20294      * The drag drop group to handle drop events for
20295      */
20296      
20297     /**
20298      * @cfg {String} dropAllowed
20299      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20300      */
20301     dropAllowed : "x-dd-drop-ok",
20302     /**
20303      * @cfg {String} dropNotAllowed
20304      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20305      */
20306     dropNotAllowed : "x-dd-drop-nodrop",
20307     /**
20308      * @cfg {boolean} success
20309      * set this after drop listener.. 
20310      */
20311     success : false,
20312     /**
20313      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20314      * if the drop point is valid for over/enter..
20315      */
20316     valid : false,
20317     // private
20318     isTarget : true,
20319
20320     // private
20321     isNotifyTarget : true,
20322     
20323     /**
20324      * @hide
20325      */
20326     notifyEnter : function(dd, e, data)
20327     {
20328         this.valid = true;
20329         this.fireEvent('enter', dd, e, data);
20330         if(this.overClass){
20331             this.el.addClass(this.overClass);
20332         }
20333         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20334             this.valid ? this.dropAllowed : this.dropNotAllowed
20335         );
20336     },
20337
20338     /**
20339      * @hide
20340      */
20341     notifyOver : function(dd, e, data)
20342     {
20343         this.valid = true;
20344         this.fireEvent('over', dd, e, data);
20345         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20346             this.valid ? this.dropAllowed : this.dropNotAllowed
20347         );
20348     },
20349
20350     /**
20351      * @hide
20352      */
20353     notifyOut : function(dd, e, data)
20354     {
20355         this.fireEvent('out', dd, e, data);
20356         if(this.overClass){
20357             this.el.removeClass(this.overClass);
20358         }
20359     },
20360
20361     /**
20362      * @hide
20363      */
20364     notifyDrop : function(dd, e, data)
20365     {
20366         this.success = false;
20367         this.fireEvent('drop', dd, e, data);
20368         return this.success;
20369     }
20370 });/*
20371  * Based on:
20372  * Ext JS Library 1.1.1
20373  * Copyright(c) 2006-2007, Ext JS, LLC.
20374  *
20375  * Originally Released Under LGPL - original licence link has changed is not relivant.
20376  *
20377  * Fork - LGPL
20378  * <script type="text/javascript">
20379  */
20380
20381
20382 /**
20383  * @class Roo.dd.DragZone
20384  * @extends Roo.dd.DragSource
20385  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20386  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20387  * @constructor
20388  * @param {String/HTMLElement/Element} el The container element
20389  * @param {Object} config
20390  */
20391 Roo.dd.DragZone = function(el, config){
20392     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20393     if(this.containerScroll){
20394         Roo.dd.ScrollManager.register(this.el);
20395     }
20396 };
20397
20398 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20399     /**
20400      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20401      * for auto scrolling during drag operations.
20402      */
20403     /**
20404      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20405      * method after a failed drop (defaults to "c3daf9" - light blue)
20406      */
20407
20408     /**
20409      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20410      * for a valid target to drag based on the mouse down. Override this method
20411      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20412      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20413      * @param {EventObject} e The mouse down event
20414      * @return {Object} The dragData
20415      */
20416     getDragData : function(e){
20417         return Roo.dd.Registry.getHandleFromEvent(e);
20418     },
20419     
20420     /**
20421      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20422      * this.dragData.ddel
20423      * @param {Number} x The x position of the click on the dragged object
20424      * @param {Number} y The y position of the click on the dragged object
20425      * @return {Boolean} true to continue the drag, false to cancel
20426      */
20427     onInitDrag : function(x, y){
20428         this.proxy.update(this.dragData.ddel.cloneNode(true));
20429         this.onStartDrag(x, y);
20430         return true;
20431     },
20432     
20433     /**
20434      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20435      */
20436     afterRepair : function(){
20437         if(Roo.enableFx){
20438             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20439         }
20440         this.dragging = false;
20441     },
20442
20443     /**
20444      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20445      * the XY of this.dragData.ddel
20446      * @param {EventObject} e The mouse up event
20447      * @return {Array} The xy location (e.g. [100, 200])
20448      */
20449     getRepairXY : function(e){
20450         return Roo.Element.fly(this.dragData.ddel).getXY();  
20451     }
20452 });/*
20453  * Based on:
20454  * Ext JS Library 1.1.1
20455  * Copyright(c) 2006-2007, Ext JS, LLC.
20456  *
20457  * Originally Released Under LGPL - original licence link has changed is not relivant.
20458  *
20459  * Fork - LGPL
20460  * <script type="text/javascript">
20461  */
20462 /**
20463  * @class Roo.dd.DropZone
20464  * @extends Roo.dd.DropTarget
20465  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20466  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20467  * @constructor
20468  * @param {String/HTMLElement/Element} el The container element
20469  * @param {Object} config
20470  */
20471 Roo.dd.DropZone = function(el, config){
20472     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20473 };
20474
20475 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20476     /**
20477      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20478      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20479      * provide your own custom lookup.
20480      * @param {Event} e The event
20481      * @return {Object} data The custom data
20482      */
20483     getTargetFromEvent : function(e){
20484         return Roo.dd.Registry.getTargetFromEvent(e);
20485     },
20486
20487     /**
20488      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20489      * that it has registered.  This method has no default implementation and should be overridden to provide
20490      * node-specific processing if necessary.
20491      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20492      * {@link #getTargetFromEvent} for this node)
20493      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20494      * @param {Event} e The event
20495      * @param {Object} data An object containing arbitrary data supplied by the drag source
20496      */
20497     onNodeEnter : function(n, dd, e, data){
20498         
20499     },
20500
20501     /**
20502      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20503      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20504      * overridden to provide the proper feedback.
20505      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20506      * {@link #getTargetFromEvent} for this node)
20507      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20508      * @param {Event} e The event
20509      * @param {Object} data An object containing arbitrary data supplied by the drag source
20510      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20511      * underlying {@link Roo.dd.StatusProxy} can be updated
20512      */
20513     onNodeOver : function(n, dd, e, data){
20514         return this.dropAllowed;
20515     },
20516
20517     /**
20518      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20519      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20520      * node-specific processing if necessary.
20521      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20522      * {@link #getTargetFromEvent} for this node)
20523      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20524      * @param {Event} e The event
20525      * @param {Object} data An object containing arbitrary data supplied by the drag source
20526      */
20527     onNodeOut : function(n, dd, e, data){
20528         
20529     },
20530
20531     /**
20532      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20533      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20534      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20535      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20536      * {@link #getTargetFromEvent} for this node)
20537      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20538      * @param {Event} e The event
20539      * @param {Object} data An object containing arbitrary data supplied by the drag source
20540      * @return {Boolean} True if the drop was valid, else false
20541      */
20542     onNodeDrop : function(n, dd, e, data){
20543         return false;
20544     },
20545
20546     /**
20547      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20548      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20549      * it should be overridden to provide the proper feedback if necessary.
20550      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20551      * @param {Event} e The event
20552      * @param {Object} data An object containing arbitrary data supplied by the drag source
20553      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20554      * underlying {@link Roo.dd.StatusProxy} can be updated
20555      */
20556     onContainerOver : function(dd, e, data){
20557         return this.dropNotAllowed;
20558     },
20559
20560     /**
20561      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20562      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20563      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20564      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20565      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20566      * @param {Event} e The event
20567      * @param {Object} data An object containing arbitrary data supplied by the drag source
20568      * @return {Boolean} True if the drop was valid, else false
20569      */
20570     onContainerDrop : function(dd, e, data){
20571         return false;
20572     },
20573
20574     /**
20575      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20576      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20577      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20578      * you should override this method and provide a custom implementation.
20579      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20580      * @param {Event} e The event
20581      * @param {Object} data An object containing arbitrary data supplied by the drag source
20582      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20583      * underlying {@link Roo.dd.StatusProxy} can be updated
20584      */
20585     notifyEnter : function(dd, e, data){
20586         return this.dropNotAllowed;
20587     },
20588
20589     /**
20590      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20591      * This method will be called on every mouse movement while the drag source is over the drop zone.
20592      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20593      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20594      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20595      * registered node, it will call {@link #onContainerOver}.
20596      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20597      * @param {Event} e The event
20598      * @param {Object} data An object containing arbitrary data supplied by the drag source
20599      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20600      * underlying {@link Roo.dd.StatusProxy} can be updated
20601      */
20602     notifyOver : function(dd, e, data){
20603         var n = this.getTargetFromEvent(e);
20604         if(!n){ // not over valid drop target
20605             if(this.lastOverNode){
20606                 this.onNodeOut(this.lastOverNode, dd, e, data);
20607                 this.lastOverNode = null;
20608             }
20609             return this.onContainerOver(dd, e, data);
20610         }
20611         if(this.lastOverNode != n){
20612             if(this.lastOverNode){
20613                 this.onNodeOut(this.lastOverNode, dd, e, data);
20614             }
20615             this.onNodeEnter(n, dd, e, data);
20616             this.lastOverNode = n;
20617         }
20618         return this.onNodeOver(n, dd, e, data);
20619     },
20620
20621     /**
20622      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20623      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20624      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20625      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20626      * @param {Event} e The event
20627      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20628      */
20629     notifyOut : function(dd, e, data){
20630         if(this.lastOverNode){
20631             this.onNodeOut(this.lastOverNode, dd, e, data);
20632             this.lastOverNode = null;
20633         }
20634     },
20635
20636     /**
20637      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20638      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20639      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20640      * otherwise it will call {@link #onContainerDrop}.
20641      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20642      * @param {Event} e The event
20643      * @param {Object} data An object containing arbitrary data supplied by the drag source
20644      * @return {Boolean} True if the drop was valid, else false
20645      */
20646     notifyDrop : function(dd, e, data){
20647         if(this.lastOverNode){
20648             this.onNodeOut(this.lastOverNode, dd, e, data);
20649             this.lastOverNode = null;
20650         }
20651         var n = this.getTargetFromEvent(e);
20652         return n ?
20653             this.onNodeDrop(n, dd, e, data) :
20654             this.onContainerDrop(dd, e, data);
20655     },
20656
20657     // private
20658     triggerCacheRefresh : function(){
20659         Roo.dd.DDM.refreshCache(this.groups);
20660     }  
20661 });/*
20662  * Based on:
20663  * Ext JS Library 1.1.1
20664  * Copyright(c) 2006-2007, Ext JS, LLC.
20665  *
20666  * Originally Released Under LGPL - original licence link has changed is not relivant.
20667  *
20668  * Fork - LGPL
20669  * <script type="text/javascript">
20670  */
20671
20672
20673 /**
20674  * @class Roo.data.SortTypes
20675  * @singleton
20676  * Defines the default sorting (casting?) comparison functions used when sorting data.
20677  */
20678 Roo.data.SortTypes = {
20679     /**
20680      * Default sort that does nothing
20681      * @param {Mixed} s The value being converted
20682      * @return {Mixed} The comparison value
20683      */
20684     none : function(s){
20685         return s;
20686     },
20687     
20688     /**
20689      * The regular expression used to strip tags
20690      * @type {RegExp}
20691      * @property
20692      */
20693     stripTagsRE : /<\/?[^>]+>/gi,
20694     
20695     /**
20696      * Strips all HTML tags to sort on text only
20697      * @param {Mixed} s The value being converted
20698      * @return {String} The comparison value
20699      */
20700     asText : function(s){
20701         return String(s).replace(this.stripTagsRE, "");
20702     },
20703     
20704     /**
20705      * Strips all HTML tags to sort on text only - Case insensitive
20706      * @param {Mixed} s The value being converted
20707      * @return {String} The comparison value
20708      */
20709     asUCText : function(s){
20710         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20711     },
20712     
20713     /**
20714      * Case insensitive string
20715      * @param {Mixed} s The value being converted
20716      * @return {String} The comparison value
20717      */
20718     asUCString : function(s) {
20719         return String(s).toUpperCase();
20720     },
20721     
20722     /**
20723      * Date sorting
20724      * @param {Mixed} s The value being converted
20725      * @return {Number} The comparison value
20726      */
20727     asDate : function(s) {
20728         if(!s){
20729             return 0;
20730         }
20731         if(s instanceof Date){
20732             return s.getTime();
20733         }
20734         return Date.parse(String(s));
20735     },
20736     
20737     /**
20738      * Float sorting
20739      * @param {Mixed} s The value being converted
20740      * @return {Float} The comparison value
20741      */
20742     asFloat : function(s) {
20743         var val = parseFloat(String(s).replace(/,/g, ""));
20744         if(isNaN(val)) val = 0;
20745         return val;
20746     },
20747     
20748     /**
20749      * Integer sorting
20750      * @param {Mixed} s The value being converted
20751      * @return {Number} The comparison value
20752      */
20753     asInt : function(s) {
20754         var val = parseInt(String(s).replace(/,/g, ""));
20755         if(isNaN(val)) val = 0;
20756         return val;
20757     }
20758 };/*
20759  * Based on:
20760  * Ext JS Library 1.1.1
20761  * Copyright(c) 2006-2007, Ext JS, LLC.
20762  *
20763  * Originally Released Under LGPL - original licence link has changed is not relivant.
20764  *
20765  * Fork - LGPL
20766  * <script type="text/javascript">
20767  */
20768
20769 /**
20770 * @class Roo.data.Record
20771  * Instances of this class encapsulate both record <em>definition</em> information, and record
20772  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20773  * to access Records cached in an {@link Roo.data.Store} object.<br>
20774  * <p>
20775  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20776  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20777  * objects.<br>
20778  * <p>
20779  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20780  * @constructor
20781  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20782  * {@link #create}. The parameters are the same.
20783  * @param {Array} data An associative Array of data values keyed by the field name.
20784  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20785  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20786  * not specified an integer id is generated.
20787  */
20788 Roo.data.Record = function(data, id){
20789     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20790     this.data = data;
20791 };
20792
20793 /**
20794  * Generate a constructor for a specific record layout.
20795  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20796  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20797  * Each field definition object may contain the following properties: <ul>
20798  * <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,
20799  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20800  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20801  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20802  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20803  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20804  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20805  * this may be omitted.</p></li>
20806  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20807  * <ul><li>auto (Default, implies no conversion)</li>
20808  * <li>string</li>
20809  * <li>int</li>
20810  * <li>float</li>
20811  * <li>boolean</li>
20812  * <li>date</li></ul></p></li>
20813  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20814  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20815  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20816  * by the Reader into an object that will be stored in the Record. It is passed the
20817  * following parameters:<ul>
20818  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20819  * </ul></p></li>
20820  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20821  * </ul>
20822  * <br>usage:<br><pre><code>
20823 var TopicRecord = Roo.data.Record.create(
20824     {name: 'title', mapping: 'topic_title'},
20825     {name: 'author', mapping: 'username'},
20826     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20827     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20828     {name: 'lastPoster', mapping: 'user2'},
20829     {name: 'excerpt', mapping: 'post_text'}
20830 );
20831
20832 var myNewRecord = new TopicRecord({
20833     title: 'Do my job please',
20834     author: 'noobie',
20835     totalPosts: 1,
20836     lastPost: new Date(),
20837     lastPoster: 'Animal',
20838     excerpt: 'No way dude!'
20839 });
20840 myStore.add(myNewRecord);
20841 </code></pre>
20842  * @method create
20843  * @static
20844  */
20845 Roo.data.Record.create = function(o){
20846     var f = function(){
20847         f.superclass.constructor.apply(this, arguments);
20848     };
20849     Roo.extend(f, Roo.data.Record);
20850     var p = f.prototype;
20851     p.fields = new Roo.util.MixedCollection(false, function(field){
20852         return field.name;
20853     });
20854     for(var i = 0, len = o.length; i < len; i++){
20855         p.fields.add(new Roo.data.Field(o[i]));
20856     }
20857     f.getField = function(name){
20858         return p.fields.get(name);  
20859     };
20860     return f;
20861 };
20862
20863 Roo.data.Record.AUTO_ID = 1000;
20864 Roo.data.Record.EDIT = 'edit';
20865 Roo.data.Record.REJECT = 'reject';
20866 Roo.data.Record.COMMIT = 'commit';
20867
20868 Roo.data.Record.prototype = {
20869     /**
20870      * Readonly flag - true if this record has been modified.
20871      * @type Boolean
20872      */
20873     dirty : false,
20874     editing : false,
20875     error: null,
20876     modified: null,
20877
20878     // private
20879     join : function(store){
20880         this.store = store;
20881     },
20882
20883     /**
20884      * Set the named field to the specified value.
20885      * @param {String} name The name of the field to set.
20886      * @param {Object} value The value to set the field to.
20887      */
20888     set : function(name, value){
20889         if(this.data[name] == value){
20890             return;
20891         }
20892         this.dirty = true;
20893         if(!this.modified){
20894             this.modified = {};
20895         }
20896         if(typeof this.modified[name] == 'undefined'){
20897             this.modified[name] = this.data[name];
20898         }
20899         this.data[name] = value;
20900         if(!this.editing && this.store){
20901             this.store.afterEdit(this);
20902         }       
20903     },
20904
20905     /**
20906      * Get the value of the named field.
20907      * @param {String} name The name of the field to get the value of.
20908      * @return {Object} The value of the field.
20909      */
20910     get : function(name){
20911         return this.data[name]; 
20912     },
20913
20914     // private
20915     beginEdit : function(){
20916         this.editing = true;
20917         this.modified = {}; 
20918     },
20919
20920     // private
20921     cancelEdit : function(){
20922         this.editing = false;
20923         delete this.modified;
20924     },
20925
20926     // private
20927     endEdit : function(){
20928         this.editing = false;
20929         if(this.dirty && this.store){
20930             this.store.afterEdit(this);
20931         }
20932     },
20933
20934     /**
20935      * Usually called by the {@link Roo.data.Store} which owns the Record.
20936      * Rejects all changes made to the Record since either creation, or the last commit operation.
20937      * Modified fields are reverted to their original values.
20938      * <p>
20939      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20940      * of reject operations.
20941      */
20942     reject : function(){
20943         var m = this.modified;
20944         for(var n in m){
20945             if(typeof m[n] != "function"){
20946                 this.data[n] = m[n];
20947             }
20948         }
20949         this.dirty = false;
20950         delete this.modified;
20951         this.editing = false;
20952         if(this.store){
20953             this.store.afterReject(this);
20954         }
20955     },
20956
20957     /**
20958      * Usually called by the {@link Roo.data.Store} which owns the Record.
20959      * Commits all changes made to the Record since either creation, or the last commit operation.
20960      * <p>
20961      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20962      * of commit operations.
20963      */
20964     commit : function(){
20965         this.dirty = false;
20966         delete this.modified;
20967         this.editing = false;
20968         if(this.store){
20969             this.store.afterCommit(this);
20970         }
20971     },
20972
20973     // private
20974     hasError : function(){
20975         return this.error != null;
20976     },
20977
20978     // private
20979     clearError : function(){
20980         this.error = null;
20981     },
20982
20983     /**
20984      * Creates a copy of this record.
20985      * @param {String} id (optional) A new record id if you don't want to use this record's id
20986      * @return {Record}
20987      */
20988     copy : function(newId) {
20989         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
20990     }
20991 };/*
20992  * Based on:
20993  * Ext JS Library 1.1.1
20994  * Copyright(c) 2006-2007, Ext JS, LLC.
20995  *
20996  * Originally Released Under LGPL - original licence link has changed is not relivant.
20997  *
20998  * Fork - LGPL
20999  * <script type="text/javascript">
21000  */
21001
21002
21003
21004 /**
21005  * @class Roo.data.Store
21006  * @extends Roo.util.Observable
21007  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21008  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21009  * <p>
21010  * 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
21011  * has no knowledge of the format of the data returned by the Proxy.<br>
21012  * <p>
21013  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21014  * instances from the data object. These records are cached and made available through accessor functions.
21015  * @constructor
21016  * Creates a new Store.
21017  * @param {Object} config A config object containing the objects needed for the Store to access data,
21018  * and read the data into Records.
21019  */
21020 Roo.data.Store = function(config){
21021     this.data = new Roo.util.MixedCollection(false);
21022     this.data.getKey = function(o){
21023         return o.id;
21024     };
21025     this.baseParams = {};
21026     // private
21027     this.paramNames = {
21028         "start" : "start",
21029         "limit" : "limit",
21030         "sort" : "sort",
21031         "dir" : "dir",
21032         "multisort" : "_multisort"
21033     };
21034
21035     if(config && config.data){
21036         this.inlineData = config.data;
21037         delete config.data;
21038     }
21039
21040     Roo.apply(this, config);
21041     
21042     if(this.reader){ // reader passed
21043         this.reader = Roo.factory(this.reader, Roo.data);
21044         this.reader.xmodule = this.xmodule || false;
21045         if(!this.recordType){
21046             this.recordType = this.reader.recordType;
21047         }
21048         if(this.reader.onMetaChange){
21049             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21050         }
21051     }
21052
21053     if(this.recordType){
21054         this.fields = this.recordType.prototype.fields;
21055     }
21056     this.modified = [];
21057
21058     this.addEvents({
21059         /**
21060          * @event datachanged
21061          * Fires when the data cache has changed, and a widget which is using this Store
21062          * as a Record cache should refresh its view.
21063          * @param {Store} this
21064          */
21065         datachanged : true,
21066         /**
21067          * @event metachange
21068          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21069          * @param {Store} this
21070          * @param {Object} meta The JSON metadata
21071          */
21072         metachange : true,
21073         /**
21074          * @event add
21075          * Fires when Records have been added to the Store
21076          * @param {Store} this
21077          * @param {Roo.data.Record[]} records The array of Records added
21078          * @param {Number} index The index at which the record(s) were added
21079          */
21080         add : true,
21081         /**
21082          * @event remove
21083          * Fires when a Record has been removed from the Store
21084          * @param {Store} this
21085          * @param {Roo.data.Record} record The Record that was removed
21086          * @param {Number} index The index at which the record was removed
21087          */
21088         remove : true,
21089         /**
21090          * @event update
21091          * Fires when a Record has been updated
21092          * @param {Store} this
21093          * @param {Roo.data.Record} record The Record that was updated
21094          * @param {String} operation The update operation being performed.  Value may be one of:
21095          * <pre><code>
21096  Roo.data.Record.EDIT
21097  Roo.data.Record.REJECT
21098  Roo.data.Record.COMMIT
21099          * </code></pre>
21100          */
21101         update : true,
21102         /**
21103          * @event clear
21104          * Fires when the data cache has been cleared.
21105          * @param {Store} this
21106          */
21107         clear : true,
21108         /**
21109          * @event beforeload
21110          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21111          * the load action will be canceled.
21112          * @param {Store} this
21113          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21114          */
21115         beforeload : true,
21116         /**
21117          * @event beforeloadadd
21118          * Fires after a new set of Records has been loaded.
21119          * @param {Store} this
21120          * @param {Roo.data.Record[]} records The Records that were loaded
21121          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21122          */
21123         beforeloadadd : true,
21124         /**
21125          * @event load
21126          * Fires after a new set of Records has been loaded, before they are added to the store.
21127          * @param {Store} this
21128          * @param {Roo.data.Record[]} records The Records that were loaded
21129          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21130          * @params {Object} return from reader
21131          */
21132         load : true,
21133         /**
21134          * @event loadexception
21135          * Fires if an exception occurs in the Proxy during loading.
21136          * Called with the signature of the Proxy's "loadexception" event.
21137          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21138          * 
21139          * @param {Proxy} 
21140          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21141          * @param {Object} load options 
21142          * @param {Object} jsonData from your request (normally this contains the Exception)
21143          */
21144         loadexception : true
21145     });
21146     
21147     if(this.proxy){
21148         this.proxy = Roo.factory(this.proxy, Roo.data);
21149         this.proxy.xmodule = this.xmodule || false;
21150         this.relayEvents(this.proxy,  ["loadexception"]);
21151     }
21152     this.sortToggle = {};
21153     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21154
21155     Roo.data.Store.superclass.constructor.call(this);
21156
21157     if(this.inlineData){
21158         this.loadData(this.inlineData);
21159         delete this.inlineData;
21160     }
21161 };
21162
21163 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21164      /**
21165     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21166     * without a remote query - used by combo/forms at present.
21167     */
21168     
21169     /**
21170     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21171     */
21172     /**
21173     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21174     */
21175     /**
21176     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21177     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21178     */
21179     /**
21180     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21181     * on any HTTP request
21182     */
21183     /**
21184     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21185     */
21186     /**
21187     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21188     */
21189     multiSort: false,
21190     /**
21191     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21192     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21193     */
21194     remoteSort : false,
21195
21196     /**
21197     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21198      * loaded or when a record is removed. (defaults to false).
21199     */
21200     pruneModifiedRecords : false,
21201
21202     // private
21203     lastOptions : null,
21204
21205     /**
21206      * Add Records to the Store and fires the add event.
21207      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21208      */
21209     add : function(records){
21210         records = [].concat(records);
21211         for(var i = 0, len = records.length; i < len; i++){
21212             records[i].join(this);
21213         }
21214         var index = this.data.length;
21215         this.data.addAll(records);
21216         this.fireEvent("add", this, records, index);
21217     },
21218
21219     /**
21220      * Remove a Record from the Store and fires the remove event.
21221      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21222      */
21223     remove : function(record){
21224         var index = this.data.indexOf(record);
21225         this.data.removeAt(index);
21226         if(this.pruneModifiedRecords){
21227             this.modified.remove(record);
21228         }
21229         this.fireEvent("remove", this, record, index);
21230     },
21231
21232     /**
21233      * Remove all Records from the Store and fires the clear event.
21234      */
21235     removeAll : function(){
21236         this.data.clear();
21237         if(this.pruneModifiedRecords){
21238             this.modified = [];
21239         }
21240         this.fireEvent("clear", this);
21241     },
21242
21243     /**
21244      * Inserts Records to the Store at the given index and fires the add event.
21245      * @param {Number} index The start index at which to insert the passed Records.
21246      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21247      */
21248     insert : function(index, records){
21249         records = [].concat(records);
21250         for(var i = 0, len = records.length; i < len; i++){
21251             this.data.insert(index, records[i]);
21252             records[i].join(this);
21253         }
21254         this.fireEvent("add", this, records, index);
21255     },
21256
21257     /**
21258      * Get the index within the cache of the passed Record.
21259      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21260      * @return {Number} The index of the passed Record. Returns -1 if not found.
21261      */
21262     indexOf : function(record){
21263         return this.data.indexOf(record);
21264     },
21265
21266     /**
21267      * Get the index within the cache of the Record with the passed id.
21268      * @param {String} id The id of the Record to find.
21269      * @return {Number} The index of the Record. Returns -1 if not found.
21270      */
21271     indexOfId : function(id){
21272         return this.data.indexOfKey(id);
21273     },
21274
21275     /**
21276      * Get the Record with the specified id.
21277      * @param {String} id The id of the Record to find.
21278      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21279      */
21280     getById : function(id){
21281         return this.data.key(id);
21282     },
21283
21284     /**
21285      * Get the Record at the specified index.
21286      * @param {Number} index The index of the Record to find.
21287      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21288      */
21289     getAt : function(index){
21290         return this.data.itemAt(index);
21291     },
21292
21293     /**
21294      * Returns a range of Records between specified indices.
21295      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21296      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21297      * @return {Roo.data.Record[]} An array of Records
21298      */
21299     getRange : function(start, end){
21300         return this.data.getRange(start, end);
21301     },
21302
21303     // private
21304     storeOptions : function(o){
21305         o = Roo.apply({}, o);
21306         delete o.callback;
21307         delete o.scope;
21308         this.lastOptions = o;
21309     },
21310
21311     /**
21312      * Loads the Record cache from the configured Proxy using the configured Reader.
21313      * <p>
21314      * If using remote paging, then the first load call must specify the <em>start</em>
21315      * and <em>limit</em> properties in the options.params property to establish the initial
21316      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21317      * <p>
21318      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21319      * and this call will return before the new data has been loaded. Perform any post-processing
21320      * in a callback function, or in a "load" event handler.</strong>
21321      * <p>
21322      * @param {Object} options An object containing properties which control loading options:<ul>
21323      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21324      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21325      * passed the following arguments:<ul>
21326      * <li>r : Roo.data.Record[]</li>
21327      * <li>options: Options object from the load call</li>
21328      * <li>success: Boolean success indicator</li></ul></li>
21329      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21330      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21331      * </ul>
21332      */
21333     load : function(options){
21334         options = options || {};
21335         if(this.fireEvent("beforeload", this, options) !== false){
21336             this.storeOptions(options);
21337             var p = Roo.apply(options.params || {}, this.baseParams);
21338             // if meta was not loaded from remote source.. try requesting it.
21339             if (!this.reader.metaFromRemote) {
21340                 p._requestMeta = 1;
21341             }
21342             if(this.sortInfo && this.remoteSort){
21343                 var pn = this.paramNames;
21344                 p[pn["sort"]] = this.sortInfo.field;
21345                 p[pn["dir"]] = this.sortInfo.direction;
21346             }
21347             if (this.multiSort) {
21348                 var pn = this.paramNames;
21349                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21350             }
21351             
21352             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21353         }
21354     },
21355
21356     /**
21357      * Reloads the Record cache from the configured Proxy using the configured Reader and
21358      * the options from the last load operation performed.
21359      * @param {Object} options (optional) An object containing properties which may override the options
21360      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21361      * the most recently used options are reused).
21362      */
21363     reload : function(options){
21364         this.load(Roo.applyIf(options||{}, this.lastOptions));
21365     },
21366
21367     // private
21368     // Called as a callback by the Reader during a load operation.
21369     loadRecords : function(o, options, success){
21370         if(!o || success === false){
21371             if(success !== false){
21372                 this.fireEvent("load", this, [], options, o);
21373             }
21374             if(options.callback){
21375                 options.callback.call(options.scope || this, [], options, false);
21376             }
21377             return;
21378         }
21379         // if data returned failure - throw an exception.
21380         if (o.success === false) {
21381             // show a message if no listener is registered.
21382             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21383                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21384             }
21385             // loadmask wil be hooked into this..
21386             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21387             return;
21388         }
21389         var r = o.records, t = o.totalRecords || r.length;
21390         
21391         this.fireEvent("beforeloadadd", this, r, options, o);
21392         
21393         if(!options || options.add !== true){
21394             if(this.pruneModifiedRecords){
21395                 this.modified = [];
21396             }
21397             for(var i = 0, len = r.length; i < len; i++){
21398                 r[i].join(this);
21399             }
21400             if(this.snapshot){
21401                 this.data = this.snapshot;
21402                 delete this.snapshot;
21403             }
21404             this.data.clear();
21405             this.data.addAll(r);
21406             this.totalLength = t;
21407             this.applySort();
21408             this.fireEvent("datachanged", this);
21409         }else{
21410             this.totalLength = Math.max(t, this.data.length+r.length);
21411             this.add(r);
21412         }
21413         this.fireEvent("load", this, r, options, o);
21414         if(options.callback){
21415             options.callback.call(options.scope || this, r, options, true);
21416         }
21417     },
21418
21419
21420     /**
21421      * Loads data from a passed data block. A Reader which understands the format of the data
21422      * must have been configured in the constructor.
21423      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21424      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21425      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21426      */
21427     loadData : function(o, append){
21428         var r = this.reader.readRecords(o);
21429         this.loadRecords(r, {add: append}, true);
21430     },
21431
21432     /**
21433      * Gets the number of cached records.
21434      * <p>
21435      * <em>If using paging, this may not be the total size of the dataset. If the data object
21436      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21437      * the data set size</em>
21438      */
21439     getCount : function(){
21440         return this.data.length || 0;
21441     },
21442
21443     /**
21444      * Gets the total number of records in the dataset as returned by the server.
21445      * <p>
21446      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21447      * the dataset size</em>
21448      */
21449     getTotalCount : function(){
21450         return this.totalLength || 0;
21451     },
21452
21453     /**
21454      * Returns the sort state of the Store as an object with two properties:
21455      * <pre><code>
21456  field {String} The name of the field by which the Records are sorted
21457  direction {String} The sort order, "ASC" or "DESC"
21458      * </code></pre>
21459      */
21460     getSortState : function(){
21461         return this.sortInfo;
21462     },
21463
21464     // private
21465     applySort : function(){
21466         if(this.sortInfo && !this.remoteSort){
21467             var s = this.sortInfo, f = s.field;
21468             var st = this.fields.get(f).sortType;
21469             var fn = function(r1, r2){
21470                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21471                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21472             };
21473             this.data.sort(s.direction, fn);
21474             if(this.snapshot && this.snapshot != this.data){
21475                 this.snapshot.sort(s.direction, fn);
21476             }
21477         }
21478     },
21479
21480     /**
21481      * Sets the default sort column and order to be used by the next load operation.
21482      * @param {String} fieldName The name of the field to sort by.
21483      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21484      */
21485     setDefaultSort : function(field, dir){
21486         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21487     },
21488
21489     /**
21490      * Sort the Records.
21491      * If remote sorting is used, the sort is performed on the server, and the cache is
21492      * reloaded. If local sorting is used, the cache is sorted internally.
21493      * @param {String} fieldName The name of the field to sort by.
21494      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21495      */
21496     sort : function(fieldName, dir){
21497         var f = this.fields.get(fieldName);
21498         if(!dir){
21499             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21500             
21501             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21502                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21503             }else{
21504                 dir = f.sortDir;
21505             }
21506         }
21507         this.sortToggle[f.name] = dir;
21508         this.sortInfo = {field: f.name, direction: dir};
21509         if(!this.remoteSort){
21510             this.applySort();
21511             this.fireEvent("datachanged", this);
21512         }else{
21513             this.load(this.lastOptions);
21514         }
21515     },
21516
21517     /**
21518      * Calls the specified function for each of the Records in the cache.
21519      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21520      * Returning <em>false</em> aborts and exits the iteration.
21521      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21522      */
21523     each : function(fn, scope){
21524         this.data.each(fn, scope);
21525     },
21526
21527     /**
21528      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21529      * (e.g., during paging).
21530      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21531      */
21532     getModifiedRecords : function(){
21533         return this.modified;
21534     },
21535
21536     // private
21537     createFilterFn : function(property, value, anyMatch){
21538         if(!value.exec){ // not a regex
21539             value = String(value);
21540             if(value.length == 0){
21541                 return false;
21542             }
21543             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21544         }
21545         return function(r){
21546             return value.test(r.data[property]);
21547         };
21548     },
21549
21550     /**
21551      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21552      * @param {String} property A field on your records
21553      * @param {Number} start The record index to start at (defaults to 0)
21554      * @param {Number} end The last record index to include (defaults to length - 1)
21555      * @return {Number} The sum
21556      */
21557     sum : function(property, start, end){
21558         var rs = this.data.items, v = 0;
21559         start = start || 0;
21560         end = (end || end === 0) ? end : rs.length-1;
21561
21562         for(var i = start; i <= end; i++){
21563             v += (rs[i].data[property] || 0);
21564         }
21565         return v;
21566     },
21567
21568     /**
21569      * Filter the records by a specified property.
21570      * @param {String} field A field on your records
21571      * @param {String/RegExp} value Either a string that the field
21572      * should start with or a RegExp to test against the field
21573      * @param {Boolean} anyMatch True to match any part not just the beginning
21574      */
21575     filter : function(property, value, anyMatch){
21576         var fn = this.createFilterFn(property, value, anyMatch);
21577         return fn ? this.filterBy(fn) : this.clearFilter();
21578     },
21579
21580     /**
21581      * Filter by a function. The specified function will be called with each
21582      * record in this data source. If the function returns true the record is included,
21583      * otherwise it is filtered.
21584      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21585      * @param {Object} scope (optional) The scope of the function (defaults to this)
21586      */
21587     filterBy : function(fn, scope){
21588         this.snapshot = this.snapshot || this.data;
21589         this.data = this.queryBy(fn, scope||this);
21590         this.fireEvent("datachanged", this);
21591     },
21592
21593     /**
21594      * Query the records by a specified property.
21595      * @param {String} field A field on your records
21596      * @param {String/RegExp} value Either a string that the field
21597      * should start with or a RegExp to test against the field
21598      * @param {Boolean} anyMatch True to match any part not just the beginning
21599      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21600      */
21601     query : function(property, value, anyMatch){
21602         var fn = this.createFilterFn(property, value, anyMatch);
21603         return fn ? this.queryBy(fn) : this.data.clone();
21604     },
21605
21606     /**
21607      * Query by a function. The specified function will be called with each
21608      * record in this data source. If the function returns true the record is included
21609      * in the results.
21610      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21611      * @param {Object} scope (optional) The scope of the function (defaults to this)
21612       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21613      **/
21614     queryBy : function(fn, scope){
21615         var data = this.snapshot || this.data;
21616         return data.filterBy(fn, scope||this);
21617     },
21618
21619     /**
21620      * Collects unique values for a particular dataIndex from this store.
21621      * @param {String} dataIndex The property to collect
21622      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21623      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21624      * @return {Array} An array of the unique values
21625      **/
21626     collect : function(dataIndex, allowNull, bypassFilter){
21627         var d = (bypassFilter === true && this.snapshot) ?
21628                 this.snapshot.items : this.data.items;
21629         var v, sv, r = [], l = {};
21630         for(var i = 0, len = d.length; i < len; i++){
21631             v = d[i].data[dataIndex];
21632             sv = String(v);
21633             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21634                 l[sv] = true;
21635                 r[r.length] = v;
21636             }
21637         }
21638         return r;
21639     },
21640
21641     /**
21642      * Revert to a view of the Record cache with no filtering applied.
21643      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21644      */
21645     clearFilter : function(suppressEvent){
21646         if(this.snapshot && this.snapshot != this.data){
21647             this.data = this.snapshot;
21648             delete this.snapshot;
21649             if(suppressEvent !== true){
21650                 this.fireEvent("datachanged", this);
21651             }
21652         }
21653     },
21654
21655     // private
21656     afterEdit : function(record){
21657         if(this.modified.indexOf(record) == -1){
21658             this.modified.push(record);
21659         }
21660         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21661     },
21662     
21663     // private
21664     afterReject : function(record){
21665         this.modified.remove(record);
21666         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21667     },
21668
21669     // private
21670     afterCommit : function(record){
21671         this.modified.remove(record);
21672         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21673     },
21674
21675     /**
21676      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21677      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21678      */
21679     commitChanges : function(){
21680         var m = this.modified.slice(0);
21681         this.modified = [];
21682         for(var i = 0, len = m.length; i < len; i++){
21683             m[i].commit();
21684         }
21685     },
21686
21687     /**
21688      * Cancel outstanding changes on all changed records.
21689      */
21690     rejectChanges : function(){
21691         var m = this.modified.slice(0);
21692         this.modified = [];
21693         for(var i = 0, len = m.length; i < len; i++){
21694             m[i].reject();
21695         }
21696     },
21697
21698     onMetaChange : function(meta, rtype, o){
21699         this.recordType = rtype;
21700         this.fields = rtype.prototype.fields;
21701         delete this.snapshot;
21702         this.sortInfo = meta.sortInfo || this.sortInfo;
21703         this.modified = [];
21704         this.fireEvent('metachange', this, this.reader.meta);
21705     }
21706 });/*
21707  * Based on:
21708  * Ext JS Library 1.1.1
21709  * Copyright(c) 2006-2007, Ext JS, LLC.
21710  *
21711  * Originally Released Under LGPL - original licence link has changed is not relivant.
21712  *
21713  * Fork - LGPL
21714  * <script type="text/javascript">
21715  */
21716
21717 /**
21718  * @class Roo.data.SimpleStore
21719  * @extends Roo.data.Store
21720  * Small helper class to make creating Stores from Array data easier.
21721  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21722  * @cfg {Array} fields An array of field definition objects, or field name strings.
21723  * @cfg {Array} data The multi-dimensional array of data
21724  * @constructor
21725  * @param {Object} config
21726  */
21727 Roo.data.SimpleStore = function(config){
21728     Roo.data.SimpleStore.superclass.constructor.call(this, {
21729         isLocal : true,
21730         reader: new Roo.data.ArrayReader({
21731                 id: config.id
21732             },
21733             Roo.data.Record.create(config.fields)
21734         ),
21735         proxy : new Roo.data.MemoryProxy(config.data)
21736     });
21737     this.load();
21738 };
21739 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21740  * Based on:
21741  * Ext JS Library 1.1.1
21742  * Copyright(c) 2006-2007, Ext JS, LLC.
21743  *
21744  * Originally Released Under LGPL - original licence link has changed is not relivant.
21745  *
21746  * Fork - LGPL
21747  * <script type="text/javascript">
21748  */
21749
21750 /**
21751 /**
21752  * @extends Roo.data.Store
21753  * @class Roo.data.JsonStore
21754  * Small helper class to make creating Stores for JSON data easier. <br/>
21755 <pre><code>
21756 var store = new Roo.data.JsonStore({
21757     url: 'get-images.php',
21758     root: 'images',
21759     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21760 });
21761 </code></pre>
21762  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21763  * JsonReader and HttpProxy (unless inline data is provided).</b>
21764  * @cfg {Array} fields An array of field definition objects, or field name strings.
21765  * @constructor
21766  * @param {Object} config
21767  */
21768 Roo.data.JsonStore = function(c){
21769     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21770         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21771         reader: new Roo.data.JsonReader(c, c.fields)
21772     }));
21773 };
21774 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21775  * Based on:
21776  * Ext JS Library 1.1.1
21777  * Copyright(c) 2006-2007, Ext JS, LLC.
21778  *
21779  * Originally Released Under LGPL - original licence link has changed is not relivant.
21780  *
21781  * Fork - LGPL
21782  * <script type="text/javascript">
21783  */
21784
21785  
21786 Roo.data.Field = function(config){
21787     if(typeof config == "string"){
21788         config = {name: config};
21789     }
21790     Roo.apply(this, config);
21791     
21792     if(!this.type){
21793         this.type = "auto";
21794     }
21795     
21796     var st = Roo.data.SortTypes;
21797     // named sortTypes are supported, here we look them up
21798     if(typeof this.sortType == "string"){
21799         this.sortType = st[this.sortType];
21800     }
21801     
21802     // set default sortType for strings and dates
21803     if(!this.sortType){
21804         switch(this.type){
21805             case "string":
21806                 this.sortType = st.asUCString;
21807                 break;
21808             case "date":
21809                 this.sortType = st.asDate;
21810                 break;
21811             default:
21812                 this.sortType = st.none;
21813         }
21814     }
21815
21816     // define once
21817     var stripRe = /[\$,%]/g;
21818
21819     // prebuilt conversion function for this field, instead of
21820     // switching every time we're reading a value
21821     if(!this.convert){
21822         var cv, dateFormat = this.dateFormat;
21823         switch(this.type){
21824             case "":
21825             case "auto":
21826             case undefined:
21827                 cv = function(v){ return v; };
21828                 break;
21829             case "string":
21830                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21831                 break;
21832             case "int":
21833                 cv = function(v){
21834                     return v !== undefined && v !== null && v !== '' ?
21835                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21836                     };
21837                 break;
21838             case "float":
21839                 cv = function(v){
21840                     return v !== undefined && v !== null && v !== '' ?
21841                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21842                     };
21843                 break;
21844             case "bool":
21845             case "boolean":
21846                 cv = function(v){ return v === true || v === "true" || v == 1; };
21847                 break;
21848             case "date":
21849                 cv = function(v){
21850                     if(!v){
21851                         return '';
21852                     }
21853                     if(v instanceof Date){
21854                         return v;
21855                     }
21856                     if(dateFormat){
21857                         if(dateFormat == "timestamp"){
21858                             return new Date(v*1000);
21859                         }
21860                         return Date.parseDate(v, dateFormat);
21861                     }
21862                     var parsed = Date.parse(v);
21863                     return parsed ? new Date(parsed) : null;
21864                 };
21865              break;
21866             
21867         }
21868         this.convert = cv;
21869     }
21870 };
21871
21872 Roo.data.Field.prototype = {
21873     dateFormat: null,
21874     defaultValue: "",
21875     mapping: null,
21876     sortType : null,
21877     sortDir : "ASC"
21878 };/*
21879  * Based on:
21880  * Ext JS Library 1.1.1
21881  * Copyright(c) 2006-2007, Ext JS, LLC.
21882  *
21883  * Originally Released Under LGPL - original licence link has changed is not relivant.
21884  *
21885  * Fork - LGPL
21886  * <script type="text/javascript">
21887  */
21888  
21889 // Base class for reading structured data from a data source.  This class is intended to be
21890 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21891
21892 /**
21893  * @class Roo.data.DataReader
21894  * Base class for reading structured data from a data source.  This class is intended to be
21895  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21896  */
21897
21898 Roo.data.DataReader = function(meta, recordType){
21899     
21900     this.meta = meta;
21901     
21902     this.recordType = recordType instanceof Array ? 
21903         Roo.data.Record.create(recordType) : recordType;
21904 };
21905
21906 Roo.data.DataReader.prototype = {
21907      /**
21908      * Create an empty record
21909      * @param {Object} data (optional) - overlay some values
21910      * @return {Roo.data.Record} record created.
21911      */
21912     newRow :  function(d) {
21913         var da =  {};
21914         this.recordType.prototype.fields.each(function(c) {
21915             switch( c.type) {
21916                 case 'int' : da[c.name] = 0; break;
21917                 case 'date' : da[c.name] = new Date(); break;
21918                 case 'float' : da[c.name] = 0.0; break;
21919                 case 'boolean' : da[c.name] = false; break;
21920                 default : da[c.name] = ""; break;
21921             }
21922             
21923         });
21924         return new this.recordType(Roo.apply(da, d));
21925     }
21926     
21927 };/*
21928  * Based on:
21929  * Ext JS Library 1.1.1
21930  * Copyright(c) 2006-2007, Ext JS, LLC.
21931  *
21932  * Originally Released Under LGPL - original licence link has changed is not relivant.
21933  *
21934  * Fork - LGPL
21935  * <script type="text/javascript">
21936  */
21937
21938 /**
21939  * @class Roo.data.DataProxy
21940  * @extends Roo.data.Observable
21941  * This class is an abstract base class for implementations which provide retrieval of
21942  * unformatted data objects.<br>
21943  * <p>
21944  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21945  * (of the appropriate type which knows how to parse the data object) to provide a block of
21946  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21947  * <p>
21948  * Custom implementations must implement the load method as described in
21949  * {@link Roo.data.HttpProxy#load}.
21950  */
21951 Roo.data.DataProxy = function(){
21952     this.addEvents({
21953         /**
21954          * @event beforeload
21955          * Fires before a network request is made to retrieve a data object.
21956          * @param {Object} This DataProxy object.
21957          * @param {Object} params The params parameter to the load function.
21958          */
21959         beforeload : true,
21960         /**
21961          * @event load
21962          * Fires before the load method's callback is called.
21963          * @param {Object} This DataProxy object.
21964          * @param {Object} o The data object.
21965          * @param {Object} arg The callback argument object passed to the load function.
21966          */
21967         load : true,
21968         /**
21969          * @event loadexception
21970          * Fires if an Exception occurs during data retrieval.
21971          * @param {Object} This DataProxy object.
21972          * @param {Object} o The data object.
21973          * @param {Object} arg The callback argument object passed to the load function.
21974          * @param {Object} e The Exception.
21975          */
21976         loadexception : true
21977     });
21978     Roo.data.DataProxy.superclass.constructor.call(this);
21979 };
21980
21981 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
21982
21983     /**
21984      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
21985      */
21986 /*
21987  * Based on:
21988  * Ext JS Library 1.1.1
21989  * Copyright(c) 2006-2007, Ext JS, LLC.
21990  *
21991  * Originally Released Under LGPL - original licence link has changed is not relivant.
21992  *
21993  * Fork - LGPL
21994  * <script type="text/javascript">
21995  */
21996 /**
21997  * @class Roo.data.MemoryProxy
21998  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
21999  * to the Reader when its load method is called.
22000  * @constructor
22001  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22002  */
22003 Roo.data.MemoryProxy = function(data){
22004     if (data.data) {
22005         data = data.data;
22006     }
22007     Roo.data.MemoryProxy.superclass.constructor.call(this);
22008     this.data = data;
22009 };
22010
22011 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22012     /**
22013      * Load data from the requested source (in this case an in-memory
22014      * data object passed to the constructor), read the data object into
22015      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22016      * process that block using the passed callback.
22017      * @param {Object} params This parameter is not used by the MemoryProxy class.
22018      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22019      * object into a block of Roo.data.Records.
22020      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22021      * The function must be passed <ul>
22022      * <li>The Record block object</li>
22023      * <li>The "arg" argument from the load function</li>
22024      * <li>A boolean success indicator</li>
22025      * </ul>
22026      * @param {Object} scope The scope in which to call the callback
22027      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22028      */
22029     load : function(params, reader, callback, scope, arg){
22030         params = params || {};
22031         var result;
22032         try {
22033             result = reader.readRecords(this.data);
22034         }catch(e){
22035             this.fireEvent("loadexception", this, arg, null, e);
22036             callback.call(scope, null, arg, false);
22037             return;
22038         }
22039         callback.call(scope, result, arg, true);
22040     },
22041     
22042     // private
22043     update : function(params, records){
22044         
22045     }
22046 });/*
22047  * Based on:
22048  * Ext JS Library 1.1.1
22049  * Copyright(c) 2006-2007, Ext JS, LLC.
22050  *
22051  * Originally Released Under LGPL - original licence link has changed is not relivant.
22052  *
22053  * Fork - LGPL
22054  * <script type="text/javascript">
22055  */
22056 /**
22057  * @class Roo.data.HttpProxy
22058  * @extends Roo.data.DataProxy
22059  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22060  * configured to reference a certain URL.<br><br>
22061  * <p>
22062  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22063  * from which the running page was served.<br><br>
22064  * <p>
22065  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22066  * <p>
22067  * Be aware that to enable the browser to parse an XML document, the server must set
22068  * the Content-Type header in the HTTP response to "text/xml".
22069  * @constructor
22070  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22071  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22072  * will be used to make the request.
22073  */
22074 Roo.data.HttpProxy = function(conn){
22075     Roo.data.HttpProxy.superclass.constructor.call(this);
22076     // is conn a conn config or a real conn?
22077     this.conn = conn;
22078     this.useAjax = !conn || !conn.events;
22079   
22080 };
22081
22082 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22083     // thse are take from connection...
22084     
22085     /**
22086      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22087      */
22088     /**
22089      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22090      * extra parameters to each request made by this object. (defaults to undefined)
22091      */
22092     /**
22093      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22094      *  to each request made by this object. (defaults to undefined)
22095      */
22096     /**
22097      * @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)
22098      */
22099     /**
22100      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22101      */
22102      /**
22103      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22104      * @type Boolean
22105      */
22106   
22107
22108     /**
22109      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22110      * @type Boolean
22111      */
22112     /**
22113      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22114      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22115      * a finer-grained basis than the DataProxy events.
22116      */
22117     getConnection : function(){
22118         return this.useAjax ? Roo.Ajax : this.conn;
22119     },
22120
22121     /**
22122      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22123      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22124      * process that block using the passed callback.
22125      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22126      * for the request to the remote server.
22127      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22128      * object into a block of Roo.data.Records.
22129      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22130      * The function must be passed <ul>
22131      * <li>The Record block object</li>
22132      * <li>The "arg" argument from the load function</li>
22133      * <li>A boolean success indicator</li>
22134      * </ul>
22135      * @param {Object} scope The scope in which to call the callback
22136      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22137      */
22138     load : function(params, reader, callback, scope, arg){
22139         if(this.fireEvent("beforeload", this, params) !== false){
22140             var  o = {
22141                 params : params || {},
22142                 request: {
22143                     callback : callback,
22144                     scope : scope,
22145                     arg : arg
22146                 },
22147                 reader: reader,
22148                 callback : this.loadResponse,
22149                 scope: this
22150             };
22151             if(this.useAjax){
22152                 Roo.applyIf(o, this.conn);
22153                 if(this.activeRequest){
22154                     Roo.Ajax.abort(this.activeRequest);
22155                 }
22156                 this.activeRequest = Roo.Ajax.request(o);
22157             }else{
22158                 this.conn.request(o);
22159             }
22160         }else{
22161             callback.call(scope||this, null, arg, false);
22162         }
22163     },
22164
22165     // private
22166     loadResponse : function(o, success, response){
22167         delete this.activeRequest;
22168         if(!success){
22169             this.fireEvent("loadexception", this, o, response);
22170             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22171             return;
22172         }
22173         var result;
22174         try {
22175             result = o.reader.read(response);
22176         }catch(e){
22177             this.fireEvent("loadexception", this, o, response, e);
22178             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22179             return;
22180         }
22181         
22182         this.fireEvent("load", this, o, o.request.arg);
22183         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22184     },
22185
22186     // private
22187     update : function(dataSet){
22188
22189     },
22190
22191     // private
22192     updateResponse : function(dataSet){
22193
22194     }
22195 });/*
22196  * Based on:
22197  * Ext JS Library 1.1.1
22198  * Copyright(c) 2006-2007, Ext JS, LLC.
22199  *
22200  * Originally Released Under LGPL - original licence link has changed is not relivant.
22201  *
22202  * Fork - LGPL
22203  * <script type="text/javascript">
22204  */
22205
22206 /**
22207  * @class Roo.data.ScriptTagProxy
22208  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22209  * other than the originating domain of the running page.<br><br>
22210  * <p>
22211  * <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
22212  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22213  * <p>
22214  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22215  * source code that is used as the source inside a &lt;script> tag.<br><br>
22216  * <p>
22217  * In order for the browser to process the returned data, the server must wrap the data object
22218  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22219  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22220  * depending on whether the callback name was passed:
22221  * <p>
22222  * <pre><code>
22223 boolean scriptTag = false;
22224 String cb = request.getParameter("callback");
22225 if (cb != null) {
22226     scriptTag = true;
22227     response.setContentType("text/javascript");
22228 } else {
22229     response.setContentType("application/x-json");
22230 }
22231 Writer out = response.getWriter();
22232 if (scriptTag) {
22233     out.write(cb + "(");
22234 }
22235 out.print(dataBlock.toJsonString());
22236 if (scriptTag) {
22237     out.write(");");
22238 }
22239 </pre></code>
22240  *
22241  * @constructor
22242  * @param {Object} config A configuration object.
22243  */
22244 Roo.data.ScriptTagProxy = function(config){
22245     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22246     Roo.apply(this, config);
22247     this.head = document.getElementsByTagName("head")[0];
22248 };
22249
22250 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22251
22252 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22253     /**
22254      * @cfg {String} url The URL from which to request the data object.
22255      */
22256     /**
22257      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22258      */
22259     timeout : 30000,
22260     /**
22261      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22262      * the server the name of the callback function set up by the load call to process the returned data object.
22263      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22264      * javascript output which calls this named function passing the data object as its only parameter.
22265      */
22266     callbackParam : "callback",
22267     /**
22268      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22269      * name to the request.
22270      */
22271     nocache : true,
22272
22273     /**
22274      * Load data from the configured URL, read the data object into
22275      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22276      * process that block using the passed callback.
22277      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22278      * for the request to the remote server.
22279      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22280      * object into a block of Roo.data.Records.
22281      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22282      * The function must be passed <ul>
22283      * <li>The Record block object</li>
22284      * <li>The "arg" argument from the load function</li>
22285      * <li>A boolean success indicator</li>
22286      * </ul>
22287      * @param {Object} scope The scope in which to call the callback
22288      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22289      */
22290     load : function(params, reader, callback, scope, arg){
22291         if(this.fireEvent("beforeload", this, params) !== false){
22292
22293             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22294
22295             var url = this.url;
22296             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22297             if(this.nocache){
22298                 url += "&_dc=" + (new Date().getTime());
22299             }
22300             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22301             var trans = {
22302                 id : transId,
22303                 cb : "stcCallback"+transId,
22304                 scriptId : "stcScript"+transId,
22305                 params : params,
22306                 arg : arg,
22307                 url : url,
22308                 callback : callback,
22309                 scope : scope,
22310                 reader : reader
22311             };
22312             var conn = this;
22313
22314             window[trans.cb] = function(o){
22315                 conn.handleResponse(o, trans);
22316             };
22317
22318             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22319
22320             if(this.autoAbort !== false){
22321                 this.abort();
22322             }
22323
22324             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22325
22326             var script = document.createElement("script");
22327             script.setAttribute("src", url);
22328             script.setAttribute("type", "text/javascript");
22329             script.setAttribute("id", trans.scriptId);
22330             this.head.appendChild(script);
22331
22332             this.trans = trans;
22333         }else{
22334             callback.call(scope||this, null, arg, false);
22335         }
22336     },
22337
22338     // private
22339     isLoading : function(){
22340         return this.trans ? true : false;
22341     },
22342
22343     /**
22344      * Abort the current server request.
22345      */
22346     abort : function(){
22347         if(this.isLoading()){
22348             this.destroyTrans(this.trans);
22349         }
22350     },
22351
22352     // private
22353     destroyTrans : function(trans, isLoaded){
22354         this.head.removeChild(document.getElementById(trans.scriptId));
22355         clearTimeout(trans.timeoutId);
22356         if(isLoaded){
22357             window[trans.cb] = undefined;
22358             try{
22359                 delete window[trans.cb];
22360             }catch(e){}
22361         }else{
22362             // if hasn't been loaded, wait for load to remove it to prevent script error
22363             window[trans.cb] = function(){
22364                 window[trans.cb] = undefined;
22365                 try{
22366                     delete window[trans.cb];
22367                 }catch(e){}
22368             };
22369         }
22370     },
22371
22372     // private
22373     handleResponse : function(o, trans){
22374         this.trans = false;
22375         this.destroyTrans(trans, true);
22376         var result;
22377         try {
22378             result = trans.reader.readRecords(o);
22379         }catch(e){
22380             this.fireEvent("loadexception", this, o, trans.arg, e);
22381             trans.callback.call(trans.scope||window, null, trans.arg, false);
22382             return;
22383         }
22384         this.fireEvent("load", this, o, trans.arg);
22385         trans.callback.call(trans.scope||window, result, trans.arg, true);
22386     },
22387
22388     // private
22389     handleFailure : function(trans){
22390         this.trans = false;
22391         this.destroyTrans(trans, false);
22392         this.fireEvent("loadexception", this, null, trans.arg);
22393         trans.callback.call(trans.scope||window, null, trans.arg, false);
22394     }
22395 });/*
22396  * Based on:
22397  * Ext JS Library 1.1.1
22398  * Copyright(c) 2006-2007, Ext JS, LLC.
22399  *
22400  * Originally Released Under LGPL - original licence link has changed is not relivant.
22401  *
22402  * Fork - LGPL
22403  * <script type="text/javascript">
22404  */
22405
22406 /**
22407  * @class Roo.data.JsonReader
22408  * @extends Roo.data.DataReader
22409  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22410  * based on mappings in a provided Roo.data.Record constructor.
22411  * 
22412  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22413  * in the reply previously. 
22414  * 
22415  * <p>
22416  * Example code:
22417  * <pre><code>
22418 var RecordDef = Roo.data.Record.create([
22419     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22420     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22421 ]);
22422 var myReader = new Roo.data.JsonReader({
22423     totalProperty: "results",    // The property which contains the total dataset size (optional)
22424     root: "rows",                // The property which contains an Array of row objects
22425     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22426 }, RecordDef);
22427 </code></pre>
22428  * <p>
22429  * This would consume a JSON file like this:
22430  * <pre><code>
22431 { 'results': 2, 'rows': [
22432     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22433     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22434 }
22435 </code></pre>
22436  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22437  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22438  * paged from the remote server.
22439  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22440  * @cfg {String} root name of the property which contains the Array of row objects.
22441  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22442  * @constructor
22443  * Create a new JsonReader
22444  * @param {Object} meta Metadata configuration options
22445  * @param {Object} recordType Either an Array of field definition objects,
22446  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22447  */
22448 Roo.data.JsonReader = function(meta, recordType){
22449     
22450     meta = meta || {};
22451     // set some defaults:
22452     Roo.applyIf(meta, {
22453         totalProperty: 'total',
22454         successProperty : 'success',
22455         root : 'data',
22456         id : 'id'
22457     });
22458     
22459     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22460 };
22461 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22462     
22463     /**
22464      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22465      * Used by Store query builder to append _requestMeta to params.
22466      * 
22467      */
22468     metaFromRemote : false,
22469     /**
22470      * This method is only used by a DataProxy which has retrieved data from a remote server.
22471      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22472      * @return {Object} data A data block which is used by an Roo.data.Store object as
22473      * a cache of Roo.data.Records.
22474      */
22475     read : function(response){
22476         var json = response.responseText;
22477        
22478         var o = /* eval:var:o */ eval("("+json+")");
22479         if(!o) {
22480             throw {message: "JsonReader.read: Json object not found"};
22481         }
22482         
22483         if(o.metaData){
22484             
22485             delete this.ef;
22486             this.metaFromRemote = true;
22487             this.meta = o.metaData;
22488             this.recordType = Roo.data.Record.create(o.metaData.fields);
22489             this.onMetaChange(this.meta, this.recordType, o);
22490         }
22491         return this.readRecords(o);
22492     },
22493
22494     // private function a store will implement
22495     onMetaChange : function(meta, recordType, o){
22496
22497     },
22498
22499     /**
22500          * @ignore
22501          */
22502     simpleAccess: function(obj, subsc) {
22503         return obj[subsc];
22504     },
22505
22506         /**
22507          * @ignore
22508          */
22509     getJsonAccessor: function(){
22510         var re = /[\[\.]/;
22511         return function(expr) {
22512             try {
22513                 return(re.test(expr))
22514                     ? new Function("obj", "return obj." + expr)
22515                     : function(obj){
22516                         return obj[expr];
22517                     };
22518             } catch(e){}
22519             return Roo.emptyFn;
22520         };
22521     }(),
22522
22523     /**
22524      * Create a data block containing Roo.data.Records from an XML document.
22525      * @param {Object} o An object which contains an Array of row objects in the property specified
22526      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22527      * which contains the total size of the dataset.
22528      * @return {Object} data A data block which is used by an Roo.data.Store object as
22529      * a cache of Roo.data.Records.
22530      */
22531     readRecords : function(o){
22532         /**
22533          * After any data loads, the raw JSON data is available for further custom processing.
22534          * @type Object
22535          */
22536         this.o = o;
22537         var s = this.meta, Record = this.recordType,
22538             f = Record.prototype.fields, fi = f.items, fl = f.length;
22539
22540 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22541         if (!this.ef) {
22542             if(s.totalProperty) {
22543                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22544                 }
22545                 if(s.successProperty) {
22546                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22547                 }
22548                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22549                 if (s.id) {
22550                         var g = this.getJsonAccessor(s.id);
22551                         this.getId = function(rec) {
22552                                 var r = g(rec);
22553                                 return (r === undefined || r === "") ? null : r;
22554                         };
22555                 } else {
22556                         this.getId = function(){return null;};
22557                 }
22558             this.ef = [];
22559             for(var jj = 0; jj < fl; jj++){
22560                 f = fi[jj];
22561                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22562                 this.ef[jj] = this.getJsonAccessor(map);
22563             }
22564         }
22565
22566         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22567         if(s.totalProperty){
22568             var vt = parseInt(this.getTotal(o), 10);
22569             if(!isNaN(vt)){
22570                 totalRecords = vt;
22571             }
22572         }
22573         if(s.successProperty){
22574             var vs = this.getSuccess(o);
22575             if(vs === false || vs === 'false'){
22576                 success = false;
22577             }
22578         }
22579         var records = [];
22580             for(var i = 0; i < c; i++){
22581                     var n = root[i];
22582                 var values = {};
22583                 var id = this.getId(n);
22584                 for(var j = 0; j < fl; j++){
22585                     f = fi[j];
22586                 var v = this.ef[j](n);
22587                 if (!f.convert) {
22588                     Roo.log('missing convert for ' + f.name);
22589                     Roo.log(f);
22590                     continue;
22591                 }
22592                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22593                 }
22594                 var record = new Record(values, id);
22595                 record.json = n;
22596                 records[i] = record;
22597             }
22598             return {
22599             raw : o,
22600                 success : success,
22601                 records : records,
22602                 totalRecords : totalRecords
22603             };
22604     }
22605 });/*
22606  * Based on:
22607  * Ext JS Library 1.1.1
22608  * Copyright(c) 2006-2007, Ext JS, LLC.
22609  *
22610  * Originally Released Under LGPL - original licence link has changed is not relivant.
22611  *
22612  * Fork - LGPL
22613  * <script type="text/javascript">
22614  */
22615
22616 /**
22617  * @class Roo.data.XmlReader
22618  * @extends Roo.data.DataReader
22619  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22620  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22621  * <p>
22622  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22623  * header in the HTTP response must be set to "text/xml".</em>
22624  * <p>
22625  * Example code:
22626  * <pre><code>
22627 var RecordDef = Roo.data.Record.create([
22628    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22629    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22630 ]);
22631 var myReader = new Roo.data.XmlReader({
22632    totalRecords: "results", // The element which contains the total dataset size (optional)
22633    record: "row",           // The repeated element which contains row information
22634    id: "id"                 // The element within the row that provides an ID for the record (optional)
22635 }, RecordDef);
22636 </code></pre>
22637  * <p>
22638  * This would consume an XML file like this:
22639  * <pre><code>
22640 &lt;?xml?>
22641 &lt;dataset>
22642  &lt;results>2&lt;/results>
22643  &lt;row>
22644    &lt;id>1&lt;/id>
22645    &lt;name>Bill&lt;/name>
22646    &lt;occupation>Gardener&lt;/occupation>
22647  &lt;/row>
22648  &lt;row>
22649    &lt;id>2&lt;/id>
22650    &lt;name>Ben&lt;/name>
22651    &lt;occupation>Horticulturalist&lt;/occupation>
22652  &lt;/row>
22653 &lt;/dataset>
22654 </code></pre>
22655  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22656  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22657  * paged from the remote server.
22658  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22659  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22660  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22661  * a record identifier value.
22662  * @constructor
22663  * Create a new XmlReader
22664  * @param {Object} meta Metadata configuration options
22665  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22666  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22667  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22668  */
22669 Roo.data.XmlReader = function(meta, recordType){
22670     meta = meta || {};
22671     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22672 };
22673 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22674     /**
22675      * This method is only used by a DataProxy which has retrieved data from a remote server.
22676          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22677          * to contain a method called 'responseXML' that returns an XML document object.
22678      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22679      * a cache of Roo.data.Records.
22680      */
22681     read : function(response){
22682         var doc = response.responseXML;
22683         if(!doc) {
22684             throw {message: "XmlReader.read: XML Document not available"};
22685         }
22686         return this.readRecords(doc);
22687     },
22688
22689     /**
22690      * Create a data block containing Roo.data.Records from an XML document.
22691          * @param {Object} doc A parsed XML document.
22692      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22693      * a cache of Roo.data.Records.
22694      */
22695     readRecords : function(doc){
22696         /**
22697          * After any data loads/reads, the raw XML Document is available for further custom processing.
22698          * @type XMLDocument
22699          */
22700         this.xmlData = doc;
22701         var root = doc.documentElement || doc;
22702         var q = Roo.DomQuery;
22703         var recordType = this.recordType, fields = recordType.prototype.fields;
22704         var sid = this.meta.id;
22705         var totalRecords = 0, success = true;
22706         if(this.meta.totalRecords){
22707             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22708         }
22709         
22710         if(this.meta.success){
22711             var sv = q.selectValue(this.meta.success, root, true);
22712             success = sv !== false && sv !== 'false';
22713         }
22714         var records = [];
22715         var ns = q.select(this.meta.record, root);
22716         for(var i = 0, len = ns.length; i < len; i++) {
22717                 var n = ns[i];
22718                 var values = {};
22719                 var id = sid ? q.selectValue(sid, n) : undefined;
22720                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22721                     var f = fields.items[j];
22722                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22723                     v = f.convert(v);
22724                     values[f.name] = v;
22725                 }
22726                 var record = new recordType(values, id);
22727                 record.node = n;
22728                 records[records.length] = record;
22729             }
22730
22731             return {
22732                 success : success,
22733                 records : records,
22734                 totalRecords : totalRecords || records.length
22735             };
22736     }
22737 });/*
22738  * Based on:
22739  * Ext JS Library 1.1.1
22740  * Copyright(c) 2006-2007, Ext JS, LLC.
22741  *
22742  * Originally Released Under LGPL - original licence link has changed is not relivant.
22743  *
22744  * Fork - LGPL
22745  * <script type="text/javascript">
22746  */
22747
22748 /**
22749  * @class Roo.data.ArrayReader
22750  * @extends Roo.data.DataReader
22751  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22752  * Each element of that Array represents a row of data fields. The
22753  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22754  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22755  * <p>
22756  * Example code:.
22757  * <pre><code>
22758 var RecordDef = Roo.data.Record.create([
22759     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22760     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22761 ]);
22762 var myReader = new Roo.data.ArrayReader({
22763     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22764 }, RecordDef);
22765 </code></pre>
22766  * <p>
22767  * This would consume an Array like this:
22768  * <pre><code>
22769 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22770   </code></pre>
22771  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22772  * @constructor
22773  * Create a new JsonReader
22774  * @param {Object} meta Metadata configuration options.
22775  * @param {Object} recordType Either an Array of field definition objects
22776  * as specified to {@link Roo.data.Record#create},
22777  * or an {@link Roo.data.Record} object
22778  * created using {@link Roo.data.Record#create}.
22779  */
22780 Roo.data.ArrayReader = function(meta, recordType){
22781     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22782 };
22783
22784 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22785     /**
22786      * Create a data block containing Roo.data.Records from an XML document.
22787      * @param {Object} o An Array of row objects which represents the dataset.
22788      * @return {Object} data A data block which is used by an Roo.data.Store object as
22789      * a cache of Roo.data.Records.
22790      */
22791     readRecords : function(o){
22792         var sid = this.meta ? this.meta.id : null;
22793         var recordType = this.recordType, fields = recordType.prototype.fields;
22794         var records = [];
22795         var root = o;
22796             for(var i = 0; i < root.length; i++){
22797                     var n = root[i];
22798                 var values = {};
22799                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22800                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22801                 var f = fields.items[j];
22802                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22803                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22804                 v = f.convert(v);
22805                 values[f.name] = v;
22806             }
22807                 var record = new recordType(values, id);
22808                 record.json = n;
22809                 records[records.length] = record;
22810             }
22811             return {
22812                 records : records,
22813                 totalRecords : records.length
22814             };
22815     }
22816 });/*
22817  * Based on:
22818  * Ext JS Library 1.1.1
22819  * Copyright(c) 2006-2007, Ext JS, LLC.
22820  *
22821  * Originally Released Under LGPL - original licence link has changed is not relivant.
22822  *
22823  * Fork - LGPL
22824  * <script type="text/javascript">
22825  */
22826
22827
22828 /**
22829  * @class Roo.data.Tree
22830  * @extends Roo.util.Observable
22831  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22832  * in the tree have most standard DOM functionality.
22833  * @constructor
22834  * @param {Node} root (optional) The root node
22835  */
22836 Roo.data.Tree = function(root){
22837    this.nodeHash = {};
22838    /**
22839     * The root node for this tree
22840     * @type Node
22841     */
22842    this.root = null;
22843    if(root){
22844        this.setRootNode(root);
22845    }
22846    this.addEvents({
22847        /**
22848         * @event append
22849         * Fires when a new child node is appended to a node in this tree.
22850         * @param {Tree} tree The owner tree
22851         * @param {Node} parent The parent node
22852         * @param {Node} node The newly appended node
22853         * @param {Number} index The index of the newly appended node
22854         */
22855        "append" : true,
22856        /**
22857         * @event remove
22858         * Fires when a child node is removed from a node in this tree.
22859         * @param {Tree} tree The owner tree
22860         * @param {Node} parent The parent node
22861         * @param {Node} node The child node removed
22862         */
22863        "remove" : true,
22864        /**
22865         * @event move
22866         * Fires when a node is moved to a new location in the tree
22867         * @param {Tree} tree The owner tree
22868         * @param {Node} node The node moved
22869         * @param {Node} oldParent The old parent of this node
22870         * @param {Node} newParent The new parent of this node
22871         * @param {Number} index The index it was moved to
22872         */
22873        "move" : true,
22874        /**
22875         * @event insert
22876         * Fires when a new child node is inserted in a node in this tree.
22877         * @param {Tree} tree The owner tree
22878         * @param {Node} parent The parent node
22879         * @param {Node} node The child node inserted
22880         * @param {Node} refNode The child node the node was inserted before
22881         */
22882        "insert" : true,
22883        /**
22884         * @event beforeappend
22885         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22886         * @param {Tree} tree The owner tree
22887         * @param {Node} parent The parent node
22888         * @param {Node} node The child node to be appended
22889         */
22890        "beforeappend" : true,
22891        /**
22892         * @event beforeremove
22893         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22894         * @param {Tree} tree The owner tree
22895         * @param {Node} parent The parent node
22896         * @param {Node} node The child node to be removed
22897         */
22898        "beforeremove" : true,
22899        /**
22900         * @event beforemove
22901         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22902         * @param {Tree} tree The owner tree
22903         * @param {Node} node The node being moved
22904         * @param {Node} oldParent The parent of the node
22905         * @param {Node} newParent The new parent the node is moving to
22906         * @param {Number} index The index it is being moved to
22907         */
22908        "beforemove" : true,
22909        /**
22910         * @event beforeinsert
22911         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22912         * @param {Tree} tree The owner tree
22913         * @param {Node} parent The parent node
22914         * @param {Node} node The child node to be inserted
22915         * @param {Node} refNode The child node the node is being inserted before
22916         */
22917        "beforeinsert" : true
22918    });
22919
22920     Roo.data.Tree.superclass.constructor.call(this);
22921 };
22922
22923 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22924     pathSeparator: "/",
22925
22926     proxyNodeEvent : function(){
22927         return this.fireEvent.apply(this, arguments);
22928     },
22929
22930     /**
22931      * Returns the root node for this tree.
22932      * @return {Node}
22933      */
22934     getRootNode : function(){
22935         return this.root;
22936     },
22937
22938     /**
22939      * Sets the root node for this tree.
22940      * @param {Node} node
22941      * @return {Node}
22942      */
22943     setRootNode : function(node){
22944         this.root = node;
22945         node.ownerTree = this;
22946         node.isRoot = true;
22947         this.registerNode(node);
22948         return node;
22949     },
22950
22951     /**
22952      * Gets a node in this tree by its id.
22953      * @param {String} id
22954      * @return {Node}
22955      */
22956     getNodeById : function(id){
22957         return this.nodeHash[id];
22958     },
22959
22960     registerNode : function(node){
22961         this.nodeHash[node.id] = node;
22962     },
22963
22964     unregisterNode : function(node){
22965         delete this.nodeHash[node.id];
22966     },
22967
22968     toString : function(){
22969         return "[Tree"+(this.id?" "+this.id:"")+"]";
22970     }
22971 });
22972
22973 /**
22974  * @class Roo.data.Node
22975  * @extends Roo.util.Observable
22976  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
22977  * @cfg {String} id The id for this node. If one is not specified, one is generated.
22978  * @constructor
22979  * @param {Object} attributes The attributes/config for the node
22980  */
22981 Roo.data.Node = function(attributes){
22982     /**
22983      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
22984      * @type {Object}
22985      */
22986     this.attributes = attributes || {};
22987     this.leaf = this.attributes.leaf;
22988     /**
22989      * The node id. @type String
22990      */
22991     this.id = this.attributes.id;
22992     if(!this.id){
22993         this.id = Roo.id(null, "ynode-");
22994         this.attributes.id = this.id;
22995     }
22996      
22997     
22998     /**
22999      * All child nodes of this node. @type Array
23000      */
23001     this.childNodes = [];
23002     if(!this.childNodes.indexOf){ // indexOf is a must
23003         this.childNodes.indexOf = function(o){
23004             for(var i = 0, len = this.length; i < len; i++){
23005                 if(this[i] == o) {
23006                     return i;
23007                 }
23008             }
23009             return -1;
23010         };
23011     }
23012     /**
23013      * The parent node for this node. @type Node
23014      */
23015     this.parentNode = null;
23016     /**
23017      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23018      */
23019     this.firstChild = null;
23020     /**
23021      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23022      */
23023     this.lastChild = null;
23024     /**
23025      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23026      */
23027     this.previousSibling = null;
23028     /**
23029      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23030      */
23031     this.nextSibling = null;
23032
23033     this.addEvents({
23034        /**
23035         * @event append
23036         * Fires when a new child node is appended
23037         * @param {Tree} tree The owner tree
23038         * @param {Node} this This node
23039         * @param {Node} node The newly appended node
23040         * @param {Number} index The index of the newly appended node
23041         */
23042        "append" : true,
23043        /**
23044         * @event remove
23045         * Fires when a child node is removed
23046         * @param {Tree} tree The owner tree
23047         * @param {Node} this This node
23048         * @param {Node} node The removed node
23049         */
23050        "remove" : true,
23051        /**
23052         * @event move
23053         * Fires when this node is moved to a new location in the tree
23054         * @param {Tree} tree The owner tree
23055         * @param {Node} this This node
23056         * @param {Node} oldParent The old parent of this node
23057         * @param {Node} newParent The new parent of this node
23058         * @param {Number} index The index it was moved to
23059         */
23060        "move" : true,
23061        /**
23062         * @event insert
23063         * Fires when a new child node is inserted.
23064         * @param {Tree} tree The owner tree
23065         * @param {Node} this This node
23066         * @param {Node} node The child node inserted
23067         * @param {Node} refNode The child node the node was inserted before
23068         */
23069        "insert" : true,
23070        /**
23071         * @event beforeappend
23072         * Fires before a new child is appended, return false to cancel the append.
23073         * @param {Tree} tree The owner tree
23074         * @param {Node} this This node
23075         * @param {Node} node The child node to be appended
23076         */
23077        "beforeappend" : true,
23078        /**
23079         * @event beforeremove
23080         * Fires before a child is removed, return false to cancel the remove.
23081         * @param {Tree} tree The owner tree
23082         * @param {Node} this This node
23083         * @param {Node} node The child node to be removed
23084         */
23085        "beforeremove" : true,
23086        /**
23087         * @event beforemove
23088         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23089         * @param {Tree} tree The owner tree
23090         * @param {Node} this This node
23091         * @param {Node} oldParent The parent of this node
23092         * @param {Node} newParent The new parent this node is moving to
23093         * @param {Number} index The index it is being moved to
23094         */
23095        "beforemove" : true,
23096        /**
23097         * @event beforeinsert
23098         * Fires before a new child is inserted, return false to cancel the insert.
23099         * @param {Tree} tree The owner tree
23100         * @param {Node} this This node
23101         * @param {Node} node The child node to be inserted
23102         * @param {Node} refNode The child node the node is being inserted before
23103         */
23104        "beforeinsert" : true
23105    });
23106     this.listeners = this.attributes.listeners;
23107     Roo.data.Node.superclass.constructor.call(this);
23108 };
23109
23110 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23111     fireEvent : function(evtName){
23112         // first do standard event for this node
23113         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23114             return false;
23115         }
23116         // then bubble it up to the tree if the event wasn't cancelled
23117         var ot = this.getOwnerTree();
23118         if(ot){
23119             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23120                 return false;
23121             }
23122         }
23123         return true;
23124     },
23125
23126     /**
23127      * Returns true if this node is a leaf
23128      * @return {Boolean}
23129      */
23130     isLeaf : function(){
23131         return this.leaf === true;
23132     },
23133
23134     // private
23135     setFirstChild : function(node){
23136         this.firstChild = node;
23137     },
23138
23139     //private
23140     setLastChild : function(node){
23141         this.lastChild = node;
23142     },
23143
23144
23145     /**
23146      * Returns true if this node is the last child of its parent
23147      * @return {Boolean}
23148      */
23149     isLast : function(){
23150        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23151     },
23152
23153     /**
23154      * Returns true if this node is the first child of its parent
23155      * @return {Boolean}
23156      */
23157     isFirst : function(){
23158        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23159     },
23160
23161     hasChildNodes : function(){
23162         return !this.isLeaf() && this.childNodes.length > 0;
23163     },
23164
23165     /**
23166      * Insert node(s) as the last child node of this node.
23167      * @param {Node/Array} node The node or Array of nodes to append
23168      * @return {Node} The appended node if single append, or null if an array was passed
23169      */
23170     appendChild : function(node){
23171         var multi = false;
23172         if(node instanceof Array){
23173             multi = node;
23174         }else if(arguments.length > 1){
23175             multi = arguments;
23176         }
23177         // if passed an array or multiple args do them one by one
23178         if(multi){
23179             for(var i = 0, len = multi.length; i < len; i++) {
23180                 this.appendChild(multi[i]);
23181             }
23182         }else{
23183             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23184                 return false;
23185             }
23186             var index = this.childNodes.length;
23187             var oldParent = node.parentNode;
23188             // it's a move, make sure we move it cleanly
23189             if(oldParent){
23190                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23191                     return false;
23192                 }
23193                 oldParent.removeChild(node);
23194             }
23195             index = this.childNodes.length;
23196             if(index == 0){
23197                 this.setFirstChild(node);
23198             }
23199             this.childNodes.push(node);
23200             node.parentNode = this;
23201             var ps = this.childNodes[index-1];
23202             if(ps){
23203                 node.previousSibling = ps;
23204                 ps.nextSibling = node;
23205             }else{
23206                 node.previousSibling = null;
23207             }
23208             node.nextSibling = null;
23209             this.setLastChild(node);
23210             node.setOwnerTree(this.getOwnerTree());
23211             this.fireEvent("append", this.ownerTree, this, node, index);
23212             if(oldParent){
23213                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23214             }
23215             return node;
23216         }
23217     },
23218
23219     /**
23220      * Removes a child node from this node.
23221      * @param {Node} node The node to remove
23222      * @return {Node} The removed node
23223      */
23224     removeChild : function(node){
23225         var index = this.childNodes.indexOf(node);
23226         if(index == -1){
23227             return false;
23228         }
23229         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23230             return false;
23231         }
23232
23233         // remove it from childNodes collection
23234         this.childNodes.splice(index, 1);
23235
23236         // update siblings
23237         if(node.previousSibling){
23238             node.previousSibling.nextSibling = node.nextSibling;
23239         }
23240         if(node.nextSibling){
23241             node.nextSibling.previousSibling = node.previousSibling;
23242         }
23243
23244         // update child refs
23245         if(this.firstChild == node){
23246             this.setFirstChild(node.nextSibling);
23247         }
23248         if(this.lastChild == node){
23249             this.setLastChild(node.previousSibling);
23250         }
23251
23252         node.setOwnerTree(null);
23253         // clear any references from the node
23254         node.parentNode = null;
23255         node.previousSibling = null;
23256         node.nextSibling = null;
23257         this.fireEvent("remove", this.ownerTree, this, node);
23258         return node;
23259     },
23260
23261     /**
23262      * Inserts the first node before the second node in this nodes childNodes collection.
23263      * @param {Node} node The node to insert
23264      * @param {Node} refNode The node to insert before (if null the node is appended)
23265      * @return {Node} The inserted node
23266      */
23267     insertBefore : function(node, refNode){
23268         if(!refNode){ // like standard Dom, refNode can be null for append
23269             return this.appendChild(node);
23270         }
23271         // nothing to do
23272         if(node == refNode){
23273             return false;
23274         }
23275
23276         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23277             return false;
23278         }
23279         var index = this.childNodes.indexOf(refNode);
23280         var oldParent = node.parentNode;
23281         var refIndex = index;
23282
23283         // when moving internally, indexes will change after remove
23284         if(oldParent == this && this.childNodes.indexOf(node) < index){
23285             refIndex--;
23286         }
23287
23288         // it's a move, make sure we move it cleanly
23289         if(oldParent){
23290             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23291                 return false;
23292             }
23293             oldParent.removeChild(node);
23294         }
23295         if(refIndex == 0){
23296             this.setFirstChild(node);
23297         }
23298         this.childNodes.splice(refIndex, 0, node);
23299         node.parentNode = this;
23300         var ps = this.childNodes[refIndex-1];
23301         if(ps){
23302             node.previousSibling = ps;
23303             ps.nextSibling = node;
23304         }else{
23305             node.previousSibling = null;
23306         }
23307         node.nextSibling = refNode;
23308         refNode.previousSibling = node;
23309         node.setOwnerTree(this.getOwnerTree());
23310         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23311         if(oldParent){
23312             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23313         }
23314         return node;
23315     },
23316
23317     /**
23318      * Returns the child node at the specified index.
23319      * @param {Number} index
23320      * @return {Node}
23321      */
23322     item : function(index){
23323         return this.childNodes[index];
23324     },
23325
23326     /**
23327      * Replaces one child node in this node with another.
23328      * @param {Node} newChild The replacement node
23329      * @param {Node} oldChild The node to replace
23330      * @return {Node} The replaced node
23331      */
23332     replaceChild : function(newChild, oldChild){
23333         this.insertBefore(newChild, oldChild);
23334         this.removeChild(oldChild);
23335         return oldChild;
23336     },
23337
23338     /**
23339      * Returns the index of a child node
23340      * @param {Node} node
23341      * @return {Number} The index of the node or -1 if it was not found
23342      */
23343     indexOf : function(child){
23344         return this.childNodes.indexOf(child);
23345     },
23346
23347     /**
23348      * Returns the tree this node is in.
23349      * @return {Tree}
23350      */
23351     getOwnerTree : function(){
23352         // if it doesn't have one, look for one
23353         if(!this.ownerTree){
23354             var p = this;
23355             while(p){
23356                 if(p.ownerTree){
23357                     this.ownerTree = p.ownerTree;
23358                     break;
23359                 }
23360                 p = p.parentNode;
23361             }
23362         }
23363         return this.ownerTree;
23364     },
23365
23366     /**
23367      * Returns depth of this node (the root node has a depth of 0)
23368      * @return {Number}
23369      */
23370     getDepth : function(){
23371         var depth = 0;
23372         var p = this;
23373         while(p.parentNode){
23374             ++depth;
23375             p = p.parentNode;
23376         }
23377         return depth;
23378     },
23379
23380     // private
23381     setOwnerTree : function(tree){
23382         // if it's move, we need to update everyone
23383         if(tree != this.ownerTree){
23384             if(this.ownerTree){
23385                 this.ownerTree.unregisterNode(this);
23386             }
23387             this.ownerTree = tree;
23388             var cs = this.childNodes;
23389             for(var i = 0, len = cs.length; i < len; i++) {
23390                 cs[i].setOwnerTree(tree);
23391             }
23392             if(tree){
23393                 tree.registerNode(this);
23394             }
23395         }
23396     },
23397
23398     /**
23399      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23400      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23401      * @return {String} The path
23402      */
23403     getPath : function(attr){
23404         attr = attr || "id";
23405         var p = this.parentNode;
23406         var b = [this.attributes[attr]];
23407         while(p){
23408             b.unshift(p.attributes[attr]);
23409             p = p.parentNode;
23410         }
23411         var sep = this.getOwnerTree().pathSeparator;
23412         return sep + b.join(sep);
23413     },
23414
23415     /**
23416      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23417      * function call will be the scope provided or the current node. The arguments to the function
23418      * will be the args provided or the current node. If the function returns false at any point,
23419      * the bubble is stopped.
23420      * @param {Function} fn The function to call
23421      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23422      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23423      */
23424     bubble : function(fn, scope, args){
23425         var p = this;
23426         while(p){
23427             if(fn.call(scope || p, args || p) === false){
23428                 break;
23429             }
23430             p = p.parentNode;
23431         }
23432     },
23433
23434     /**
23435      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23436      * function call will be the scope provided or the current node. The arguments to the function
23437      * will be the args provided or the current node. If the function returns false at any point,
23438      * the cascade is stopped on that branch.
23439      * @param {Function} fn The function to call
23440      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23441      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23442      */
23443     cascade : function(fn, scope, args){
23444         if(fn.call(scope || this, args || this) !== false){
23445             var cs = this.childNodes;
23446             for(var i = 0, len = cs.length; i < len; i++) {
23447                 cs[i].cascade(fn, scope, args);
23448             }
23449         }
23450     },
23451
23452     /**
23453      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23454      * function call will be the scope provided or the current node. The arguments to the function
23455      * will be the args provided or the current node. If the function returns false at any point,
23456      * the iteration stops.
23457      * @param {Function} fn The function to call
23458      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23459      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23460      */
23461     eachChild : function(fn, scope, args){
23462         var cs = this.childNodes;
23463         for(var i = 0, len = cs.length; i < len; i++) {
23464                 if(fn.call(scope || this, args || cs[i]) === false){
23465                     break;
23466                 }
23467         }
23468     },
23469
23470     /**
23471      * Finds the first child that has the attribute with the specified value.
23472      * @param {String} attribute The attribute name
23473      * @param {Mixed} value The value to search for
23474      * @return {Node} The found child or null if none was found
23475      */
23476     findChild : function(attribute, value){
23477         var cs = this.childNodes;
23478         for(var i = 0, len = cs.length; i < len; i++) {
23479                 if(cs[i].attributes[attribute] == value){
23480                     return cs[i];
23481                 }
23482         }
23483         return null;
23484     },
23485
23486     /**
23487      * Finds the first child by a custom function. The child matches if the function passed
23488      * returns true.
23489      * @param {Function} fn
23490      * @param {Object} scope (optional)
23491      * @return {Node} The found child or null if none was found
23492      */
23493     findChildBy : function(fn, scope){
23494         var cs = this.childNodes;
23495         for(var i = 0, len = cs.length; i < len; i++) {
23496                 if(fn.call(scope||cs[i], cs[i]) === true){
23497                     return cs[i];
23498                 }
23499         }
23500         return null;
23501     },
23502
23503     /**
23504      * Sorts this nodes children using the supplied sort function
23505      * @param {Function} fn
23506      * @param {Object} scope (optional)
23507      */
23508     sort : function(fn, scope){
23509         var cs = this.childNodes;
23510         var len = cs.length;
23511         if(len > 0){
23512             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23513             cs.sort(sortFn);
23514             for(var i = 0; i < len; i++){
23515                 var n = cs[i];
23516                 n.previousSibling = cs[i-1];
23517                 n.nextSibling = cs[i+1];
23518                 if(i == 0){
23519                     this.setFirstChild(n);
23520                 }
23521                 if(i == len-1){
23522                     this.setLastChild(n);
23523                 }
23524             }
23525         }
23526     },
23527
23528     /**
23529      * Returns true if this node is an ancestor (at any point) of the passed node.
23530      * @param {Node} node
23531      * @return {Boolean}
23532      */
23533     contains : function(node){
23534         return node.isAncestor(this);
23535     },
23536
23537     /**
23538      * Returns true if the passed node is an ancestor (at any point) of this node.
23539      * @param {Node} node
23540      * @return {Boolean}
23541      */
23542     isAncestor : function(node){
23543         var p = this.parentNode;
23544         while(p){
23545             if(p == node){
23546                 return true;
23547             }
23548             p = p.parentNode;
23549         }
23550         return false;
23551     },
23552
23553     toString : function(){
23554         return "[Node"+(this.id?" "+this.id:"")+"]";
23555     }
23556 });/*
23557  * Based on:
23558  * Ext JS Library 1.1.1
23559  * Copyright(c) 2006-2007, Ext JS, LLC.
23560  *
23561  * Originally Released Under LGPL - original licence link has changed is not relivant.
23562  *
23563  * Fork - LGPL
23564  * <script type="text/javascript">
23565  */
23566  (function(){ 
23567 /**
23568  * @class Roo.Layer
23569  * @extends Roo.Element
23570  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23571  * automatic maintaining of shadow/shim positions.
23572  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23573  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23574  * you can pass a string with a CSS class name. False turns off the shadow.
23575  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23576  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23577  * @cfg {String} cls CSS class to add to the element
23578  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23579  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23580  * @constructor
23581  * @param {Object} config An object with config options.
23582  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23583  */
23584
23585 Roo.Layer = function(config, existingEl){
23586     config = config || {};
23587     var dh = Roo.DomHelper;
23588     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23589     if(existingEl){
23590         this.dom = Roo.getDom(existingEl);
23591     }
23592     if(!this.dom){
23593         var o = config.dh || {tag: "div", cls: "x-layer"};
23594         this.dom = dh.append(pel, o);
23595     }
23596     if(config.cls){
23597         this.addClass(config.cls);
23598     }
23599     this.constrain = config.constrain !== false;
23600     this.visibilityMode = Roo.Element.VISIBILITY;
23601     if(config.id){
23602         this.id = this.dom.id = config.id;
23603     }else{
23604         this.id = Roo.id(this.dom);
23605     }
23606     this.zindex = config.zindex || this.getZIndex();
23607     this.position("absolute", this.zindex);
23608     if(config.shadow){
23609         this.shadowOffset = config.shadowOffset || 4;
23610         this.shadow = new Roo.Shadow({
23611             offset : this.shadowOffset,
23612             mode : config.shadow
23613         });
23614     }else{
23615         this.shadowOffset = 0;
23616     }
23617     this.useShim = config.shim !== false && Roo.useShims;
23618     this.useDisplay = config.useDisplay;
23619     this.hide();
23620 };
23621
23622 var supr = Roo.Element.prototype;
23623
23624 // shims are shared among layer to keep from having 100 iframes
23625 var shims = [];
23626
23627 Roo.extend(Roo.Layer, Roo.Element, {
23628
23629     getZIndex : function(){
23630         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23631     },
23632
23633     getShim : function(){
23634         if(!this.useShim){
23635             return null;
23636         }
23637         if(this.shim){
23638             return this.shim;
23639         }
23640         var shim = shims.shift();
23641         if(!shim){
23642             shim = this.createShim();
23643             shim.enableDisplayMode('block');
23644             shim.dom.style.display = 'none';
23645             shim.dom.style.visibility = 'visible';
23646         }
23647         var pn = this.dom.parentNode;
23648         if(shim.dom.parentNode != pn){
23649             pn.insertBefore(shim.dom, this.dom);
23650         }
23651         shim.setStyle('z-index', this.getZIndex()-2);
23652         this.shim = shim;
23653         return shim;
23654     },
23655
23656     hideShim : function(){
23657         if(this.shim){
23658             this.shim.setDisplayed(false);
23659             shims.push(this.shim);
23660             delete this.shim;
23661         }
23662     },
23663
23664     disableShadow : function(){
23665         if(this.shadow){
23666             this.shadowDisabled = true;
23667             this.shadow.hide();
23668             this.lastShadowOffset = this.shadowOffset;
23669             this.shadowOffset = 0;
23670         }
23671     },
23672
23673     enableShadow : function(show){
23674         if(this.shadow){
23675             this.shadowDisabled = false;
23676             this.shadowOffset = this.lastShadowOffset;
23677             delete this.lastShadowOffset;
23678             if(show){
23679                 this.sync(true);
23680             }
23681         }
23682     },
23683
23684     // private
23685     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23686     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23687     sync : function(doShow){
23688         var sw = this.shadow;
23689         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23690             var sh = this.getShim();
23691
23692             var w = this.getWidth(),
23693                 h = this.getHeight();
23694
23695             var l = this.getLeft(true),
23696                 t = this.getTop(true);
23697
23698             if(sw && !this.shadowDisabled){
23699                 if(doShow && !sw.isVisible()){
23700                     sw.show(this);
23701                 }else{
23702                     sw.realign(l, t, w, h);
23703                 }
23704                 if(sh){
23705                     if(doShow){
23706                        sh.show();
23707                     }
23708                     // fit the shim behind the shadow, so it is shimmed too
23709                     var a = sw.adjusts, s = sh.dom.style;
23710                     s.left = (Math.min(l, l+a.l))+"px";
23711                     s.top = (Math.min(t, t+a.t))+"px";
23712                     s.width = (w+a.w)+"px";
23713                     s.height = (h+a.h)+"px";
23714                 }
23715             }else if(sh){
23716                 if(doShow){
23717                    sh.show();
23718                 }
23719                 sh.setSize(w, h);
23720                 sh.setLeftTop(l, t);
23721             }
23722             
23723         }
23724     },
23725
23726     // private
23727     destroy : function(){
23728         this.hideShim();
23729         if(this.shadow){
23730             this.shadow.hide();
23731         }
23732         this.removeAllListeners();
23733         var pn = this.dom.parentNode;
23734         if(pn){
23735             pn.removeChild(this.dom);
23736         }
23737         Roo.Element.uncache(this.id);
23738     },
23739
23740     remove : function(){
23741         this.destroy();
23742     },
23743
23744     // private
23745     beginUpdate : function(){
23746         this.updating = true;
23747     },
23748
23749     // private
23750     endUpdate : function(){
23751         this.updating = false;
23752         this.sync(true);
23753     },
23754
23755     // private
23756     hideUnders : function(negOffset){
23757         if(this.shadow){
23758             this.shadow.hide();
23759         }
23760         this.hideShim();
23761     },
23762
23763     // private
23764     constrainXY : function(){
23765         if(this.constrain){
23766             var vw = Roo.lib.Dom.getViewWidth(),
23767                 vh = Roo.lib.Dom.getViewHeight();
23768             var s = Roo.get(document).getScroll();
23769
23770             var xy = this.getXY();
23771             var x = xy[0], y = xy[1];   
23772             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23773             // only move it if it needs it
23774             var moved = false;
23775             // first validate right/bottom
23776             if((x + w) > vw+s.left){
23777                 x = vw - w - this.shadowOffset;
23778                 moved = true;
23779             }
23780             if((y + h) > vh+s.top){
23781                 y = vh - h - this.shadowOffset;
23782                 moved = true;
23783             }
23784             // then make sure top/left isn't negative
23785             if(x < s.left){
23786                 x = s.left;
23787                 moved = true;
23788             }
23789             if(y < s.top){
23790                 y = s.top;
23791                 moved = true;
23792             }
23793             if(moved){
23794                 if(this.avoidY){
23795                     var ay = this.avoidY;
23796                     if(y <= ay && (y+h) >= ay){
23797                         y = ay-h-5;   
23798                     }
23799                 }
23800                 xy = [x, y];
23801                 this.storeXY(xy);
23802                 supr.setXY.call(this, xy);
23803                 this.sync();
23804             }
23805         }
23806     },
23807
23808     isVisible : function(){
23809         return this.visible;    
23810     },
23811
23812     // private
23813     showAction : function(){
23814         this.visible = true; // track visibility to prevent getStyle calls
23815         if(this.useDisplay === true){
23816             this.setDisplayed("");
23817         }else if(this.lastXY){
23818             supr.setXY.call(this, this.lastXY);
23819         }else if(this.lastLT){
23820             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23821         }
23822     },
23823
23824     // private
23825     hideAction : function(){
23826         this.visible = false;
23827         if(this.useDisplay === true){
23828             this.setDisplayed(false);
23829         }else{
23830             this.setLeftTop(-10000,-10000);
23831         }
23832     },
23833
23834     // overridden Element method
23835     setVisible : function(v, a, d, c, e){
23836         if(v){
23837             this.showAction();
23838         }
23839         if(a && v){
23840             var cb = function(){
23841                 this.sync(true);
23842                 if(c){
23843                     c();
23844                 }
23845             }.createDelegate(this);
23846             supr.setVisible.call(this, true, true, d, cb, e);
23847         }else{
23848             if(!v){
23849                 this.hideUnders(true);
23850             }
23851             var cb = c;
23852             if(a){
23853                 cb = function(){
23854                     this.hideAction();
23855                     if(c){
23856                         c();
23857                     }
23858                 }.createDelegate(this);
23859             }
23860             supr.setVisible.call(this, v, a, d, cb, e);
23861             if(v){
23862                 this.sync(true);
23863             }else if(!a){
23864                 this.hideAction();
23865             }
23866         }
23867     },
23868
23869     storeXY : function(xy){
23870         delete this.lastLT;
23871         this.lastXY = xy;
23872     },
23873
23874     storeLeftTop : function(left, top){
23875         delete this.lastXY;
23876         this.lastLT = [left, top];
23877     },
23878
23879     // private
23880     beforeFx : function(){
23881         this.beforeAction();
23882         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23883     },
23884
23885     // private
23886     afterFx : function(){
23887         Roo.Layer.superclass.afterFx.apply(this, arguments);
23888         this.sync(this.isVisible());
23889     },
23890
23891     // private
23892     beforeAction : function(){
23893         if(!this.updating && this.shadow){
23894             this.shadow.hide();
23895         }
23896     },
23897
23898     // overridden Element method
23899     setLeft : function(left){
23900         this.storeLeftTop(left, this.getTop(true));
23901         supr.setLeft.apply(this, arguments);
23902         this.sync();
23903     },
23904
23905     setTop : function(top){
23906         this.storeLeftTop(this.getLeft(true), top);
23907         supr.setTop.apply(this, arguments);
23908         this.sync();
23909     },
23910
23911     setLeftTop : function(left, top){
23912         this.storeLeftTop(left, top);
23913         supr.setLeftTop.apply(this, arguments);
23914         this.sync();
23915     },
23916
23917     setXY : function(xy, a, d, c, e){
23918         this.fixDisplay();
23919         this.beforeAction();
23920         this.storeXY(xy);
23921         var cb = this.createCB(c);
23922         supr.setXY.call(this, xy, a, d, cb, e);
23923         if(!a){
23924             cb();
23925         }
23926     },
23927
23928     // private
23929     createCB : function(c){
23930         var el = this;
23931         return function(){
23932             el.constrainXY();
23933             el.sync(true);
23934             if(c){
23935                 c();
23936             }
23937         };
23938     },
23939
23940     // overridden Element method
23941     setX : function(x, a, d, c, e){
23942         this.setXY([x, this.getY()], a, d, c, e);
23943     },
23944
23945     // overridden Element method
23946     setY : function(y, a, d, c, e){
23947         this.setXY([this.getX(), y], a, d, c, e);
23948     },
23949
23950     // overridden Element method
23951     setSize : function(w, h, a, d, c, e){
23952         this.beforeAction();
23953         var cb = this.createCB(c);
23954         supr.setSize.call(this, w, h, a, d, cb, e);
23955         if(!a){
23956             cb();
23957         }
23958     },
23959
23960     // overridden Element method
23961     setWidth : function(w, a, d, c, e){
23962         this.beforeAction();
23963         var cb = this.createCB(c);
23964         supr.setWidth.call(this, w, a, d, cb, e);
23965         if(!a){
23966             cb();
23967         }
23968     },
23969
23970     // overridden Element method
23971     setHeight : function(h, a, d, c, e){
23972         this.beforeAction();
23973         var cb = this.createCB(c);
23974         supr.setHeight.call(this, h, a, d, cb, e);
23975         if(!a){
23976             cb();
23977         }
23978     },
23979
23980     // overridden Element method
23981     setBounds : function(x, y, w, h, a, d, c, e){
23982         this.beforeAction();
23983         var cb = this.createCB(c);
23984         if(!a){
23985             this.storeXY([x, y]);
23986             supr.setXY.call(this, [x, y]);
23987             supr.setSize.call(this, w, h, a, d, cb, e);
23988             cb();
23989         }else{
23990             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23991         }
23992         return this;
23993     },
23994     
23995     /**
23996      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23997      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23998      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23999      * @param {Number} zindex The new z-index to set
24000      * @return {this} The Layer
24001      */
24002     setZIndex : function(zindex){
24003         this.zindex = zindex;
24004         this.setStyle("z-index", zindex + 2);
24005         if(this.shadow){
24006             this.shadow.setZIndex(zindex + 1);
24007         }
24008         if(this.shim){
24009             this.shim.setStyle("z-index", zindex);
24010         }
24011     }
24012 });
24013 })();/*
24014  * Based on:
24015  * Ext JS Library 1.1.1
24016  * Copyright(c) 2006-2007, Ext JS, LLC.
24017  *
24018  * Originally Released Under LGPL - original licence link has changed is not relivant.
24019  *
24020  * Fork - LGPL
24021  * <script type="text/javascript">
24022  */
24023
24024
24025 /**
24026  * @class Roo.Shadow
24027  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24028  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24029  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24030  * @constructor
24031  * Create a new Shadow
24032  * @param {Object} config The config object
24033  */
24034 Roo.Shadow = function(config){
24035     Roo.apply(this, config);
24036     if(typeof this.mode != "string"){
24037         this.mode = this.defaultMode;
24038     }
24039     var o = this.offset, a = {h: 0};
24040     var rad = Math.floor(this.offset/2);
24041     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24042         case "drop":
24043             a.w = 0;
24044             a.l = a.t = o;
24045             a.t -= 1;
24046             if(Roo.isIE){
24047                 a.l -= this.offset + rad;
24048                 a.t -= this.offset + rad;
24049                 a.w -= rad;
24050                 a.h -= rad;
24051                 a.t += 1;
24052             }
24053         break;
24054         case "sides":
24055             a.w = (o*2);
24056             a.l = -o;
24057             a.t = o-1;
24058             if(Roo.isIE){
24059                 a.l -= (this.offset - rad);
24060                 a.t -= this.offset + rad;
24061                 a.l += 1;
24062                 a.w -= (this.offset - rad)*2;
24063                 a.w -= rad + 1;
24064                 a.h -= 1;
24065             }
24066         break;
24067         case "frame":
24068             a.w = a.h = (o*2);
24069             a.l = a.t = -o;
24070             a.t += 1;
24071             a.h -= 2;
24072             if(Roo.isIE){
24073                 a.l -= (this.offset - rad);
24074                 a.t -= (this.offset - rad);
24075                 a.l += 1;
24076                 a.w -= (this.offset + rad + 1);
24077                 a.h -= (this.offset + rad);
24078                 a.h += 1;
24079             }
24080         break;
24081     };
24082
24083     this.adjusts = a;
24084 };
24085
24086 Roo.Shadow.prototype = {
24087     /**
24088      * @cfg {String} mode
24089      * The shadow display mode.  Supports the following options:<br />
24090      * sides: Shadow displays on both sides and bottom only<br />
24091      * frame: Shadow displays equally on all four sides<br />
24092      * drop: Traditional bottom-right drop shadow (default)
24093      */
24094     /**
24095      * @cfg {String} offset
24096      * The number of pixels to offset the shadow from the element (defaults to 4)
24097      */
24098     offset: 4,
24099
24100     // private
24101     defaultMode: "drop",
24102
24103     /**
24104      * Displays the shadow under the target element
24105      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24106      */
24107     show : function(target){
24108         target = Roo.get(target);
24109         if(!this.el){
24110             this.el = Roo.Shadow.Pool.pull();
24111             if(this.el.dom.nextSibling != target.dom){
24112                 this.el.insertBefore(target);
24113             }
24114         }
24115         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24116         if(Roo.isIE){
24117             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24118         }
24119         this.realign(
24120             target.getLeft(true),
24121             target.getTop(true),
24122             target.getWidth(),
24123             target.getHeight()
24124         );
24125         this.el.dom.style.display = "block";
24126     },
24127
24128     /**
24129      * Returns true if the shadow is visible, else false
24130      */
24131     isVisible : function(){
24132         return this.el ? true : false;  
24133     },
24134
24135     /**
24136      * Direct alignment when values are already available. Show must be called at least once before
24137      * calling this method to ensure it is initialized.
24138      * @param {Number} left The target element left position
24139      * @param {Number} top The target element top position
24140      * @param {Number} width The target element width
24141      * @param {Number} height The target element height
24142      */
24143     realign : function(l, t, w, h){
24144         if(!this.el){
24145             return;
24146         }
24147         var a = this.adjusts, d = this.el.dom, s = d.style;
24148         var iea = 0;
24149         s.left = (l+a.l)+"px";
24150         s.top = (t+a.t)+"px";
24151         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24152  
24153         if(s.width != sws || s.height != shs){
24154             s.width = sws;
24155             s.height = shs;
24156             if(!Roo.isIE){
24157                 var cn = d.childNodes;
24158                 var sww = Math.max(0, (sw-12))+"px";
24159                 cn[0].childNodes[1].style.width = sww;
24160                 cn[1].childNodes[1].style.width = sww;
24161                 cn[2].childNodes[1].style.width = sww;
24162                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24163             }
24164         }
24165     },
24166
24167     /**
24168      * Hides this shadow
24169      */
24170     hide : function(){
24171         if(this.el){
24172             this.el.dom.style.display = "none";
24173             Roo.Shadow.Pool.push(this.el);
24174             delete this.el;
24175         }
24176     },
24177
24178     /**
24179      * Adjust the z-index of this shadow
24180      * @param {Number} zindex The new z-index
24181      */
24182     setZIndex : function(z){
24183         this.zIndex = z;
24184         if(this.el){
24185             this.el.setStyle("z-index", z);
24186         }
24187     }
24188 };
24189
24190 // Private utility class that manages the internal Shadow cache
24191 Roo.Shadow.Pool = function(){
24192     var p = [];
24193     var markup = Roo.isIE ?
24194                  '<div class="x-ie-shadow"></div>' :
24195                  '<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>';
24196     return {
24197         pull : function(){
24198             var sh = p.shift();
24199             if(!sh){
24200                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24201                 sh.autoBoxAdjust = false;
24202             }
24203             return sh;
24204         },
24205
24206         push : function(sh){
24207             p.push(sh);
24208         }
24209     };
24210 }();/*
24211  * Based on:
24212  * Ext JS Library 1.1.1
24213  * Copyright(c) 2006-2007, Ext JS, LLC.
24214  *
24215  * Originally Released Under LGPL - original licence link has changed is not relivant.
24216  *
24217  * Fork - LGPL
24218  * <script type="text/javascript">
24219  */
24220
24221
24222 /**
24223  * @class Roo.SplitBar
24224  * @extends Roo.util.Observable
24225  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24226  * <br><br>
24227  * Usage:
24228  * <pre><code>
24229 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24230                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24231 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24232 split.minSize = 100;
24233 split.maxSize = 600;
24234 split.animate = true;
24235 split.on('moved', splitterMoved);
24236 </code></pre>
24237  * @constructor
24238  * Create a new SplitBar
24239  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24240  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24241  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24242  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24243                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24244                         position of the SplitBar).
24245  */
24246 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24247     
24248     /** @private */
24249     this.el = Roo.get(dragElement, true);
24250     this.el.dom.unselectable = "on";
24251     /** @private */
24252     this.resizingEl = Roo.get(resizingElement, true);
24253
24254     /**
24255      * @private
24256      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24257      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24258      * @type Number
24259      */
24260     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24261     
24262     /**
24263      * The minimum size of the resizing element. (Defaults to 0)
24264      * @type Number
24265      */
24266     this.minSize = 0;
24267     
24268     /**
24269      * The maximum size of the resizing element. (Defaults to 2000)
24270      * @type Number
24271      */
24272     this.maxSize = 2000;
24273     
24274     /**
24275      * Whether to animate the transition to the new size
24276      * @type Boolean
24277      */
24278     this.animate = false;
24279     
24280     /**
24281      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24282      * @type Boolean
24283      */
24284     this.useShim = false;
24285     
24286     /** @private */
24287     this.shim = null;
24288     
24289     if(!existingProxy){
24290         /** @private */
24291         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24292     }else{
24293         this.proxy = Roo.get(existingProxy).dom;
24294     }
24295     /** @private */
24296     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24297     
24298     /** @private */
24299     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24300     
24301     /** @private */
24302     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24303     
24304     /** @private */
24305     this.dragSpecs = {};
24306     
24307     /**
24308      * @private The adapter to use to positon and resize elements
24309      */
24310     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24311     this.adapter.init(this);
24312     
24313     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24314         /** @private */
24315         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24316         this.el.addClass("x-splitbar-h");
24317     }else{
24318         /** @private */
24319         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24320         this.el.addClass("x-splitbar-v");
24321     }
24322     
24323     this.addEvents({
24324         /**
24325          * @event resize
24326          * Fires when the splitter is moved (alias for {@link #event-moved})
24327          * @param {Roo.SplitBar} this
24328          * @param {Number} newSize the new width or height
24329          */
24330         "resize" : true,
24331         /**
24332          * @event moved
24333          * Fires when the splitter is moved
24334          * @param {Roo.SplitBar} this
24335          * @param {Number} newSize the new width or height
24336          */
24337         "moved" : true,
24338         /**
24339          * @event beforeresize
24340          * Fires before the splitter is dragged
24341          * @param {Roo.SplitBar} this
24342          */
24343         "beforeresize" : true,
24344
24345         "beforeapply" : true
24346     });
24347
24348     Roo.util.Observable.call(this);
24349 };
24350
24351 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24352     onStartProxyDrag : function(x, y){
24353         this.fireEvent("beforeresize", this);
24354         if(!this.overlay){
24355             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24356             o.unselectable();
24357             o.enableDisplayMode("block");
24358             // all splitbars share the same overlay
24359             Roo.SplitBar.prototype.overlay = o;
24360         }
24361         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24362         this.overlay.show();
24363         Roo.get(this.proxy).setDisplayed("block");
24364         var size = this.adapter.getElementSize(this);
24365         this.activeMinSize = this.getMinimumSize();;
24366         this.activeMaxSize = this.getMaximumSize();;
24367         var c1 = size - this.activeMinSize;
24368         var c2 = Math.max(this.activeMaxSize - size, 0);
24369         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24370             this.dd.resetConstraints();
24371             this.dd.setXConstraint(
24372                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24373                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24374             );
24375             this.dd.setYConstraint(0, 0);
24376         }else{
24377             this.dd.resetConstraints();
24378             this.dd.setXConstraint(0, 0);
24379             this.dd.setYConstraint(
24380                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24381                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24382             );
24383          }
24384         this.dragSpecs.startSize = size;
24385         this.dragSpecs.startPoint = [x, y];
24386         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24387     },
24388     
24389     /** 
24390      * @private Called after the drag operation by the DDProxy
24391      */
24392     onEndProxyDrag : function(e){
24393         Roo.get(this.proxy).setDisplayed(false);
24394         var endPoint = Roo.lib.Event.getXY(e);
24395         if(this.overlay){
24396             this.overlay.hide();
24397         }
24398         var newSize;
24399         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24400             newSize = this.dragSpecs.startSize + 
24401                 (this.placement == Roo.SplitBar.LEFT ?
24402                     endPoint[0] - this.dragSpecs.startPoint[0] :
24403                     this.dragSpecs.startPoint[0] - endPoint[0]
24404                 );
24405         }else{
24406             newSize = this.dragSpecs.startSize + 
24407                 (this.placement == Roo.SplitBar.TOP ?
24408                     endPoint[1] - this.dragSpecs.startPoint[1] :
24409                     this.dragSpecs.startPoint[1] - endPoint[1]
24410                 );
24411         }
24412         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24413         if(newSize != this.dragSpecs.startSize){
24414             if(this.fireEvent('beforeapply', this, newSize) !== false){
24415                 this.adapter.setElementSize(this, newSize);
24416                 this.fireEvent("moved", this, newSize);
24417                 this.fireEvent("resize", this, newSize);
24418             }
24419         }
24420     },
24421     
24422     /**
24423      * Get the adapter this SplitBar uses
24424      * @return The adapter object
24425      */
24426     getAdapter : function(){
24427         return this.adapter;
24428     },
24429     
24430     /**
24431      * Set the adapter this SplitBar uses
24432      * @param {Object} adapter A SplitBar adapter object
24433      */
24434     setAdapter : function(adapter){
24435         this.adapter = adapter;
24436         this.adapter.init(this);
24437     },
24438     
24439     /**
24440      * Gets the minimum size for the resizing element
24441      * @return {Number} The minimum size
24442      */
24443     getMinimumSize : function(){
24444         return this.minSize;
24445     },
24446     
24447     /**
24448      * Sets the minimum size for the resizing element
24449      * @param {Number} minSize The minimum size
24450      */
24451     setMinimumSize : function(minSize){
24452         this.minSize = minSize;
24453     },
24454     
24455     /**
24456      * Gets the maximum size for the resizing element
24457      * @return {Number} The maximum size
24458      */
24459     getMaximumSize : function(){
24460         return this.maxSize;
24461     },
24462     
24463     /**
24464      * Sets the maximum size for the resizing element
24465      * @param {Number} maxSize The maximum size
24466      */
24467     setMaximumSize : function(maxSize){
24468         this.maxSize = maxSize;
24469     },
24470     
24471     /**
24472      * Sets the initialize size for the resizing element
24473      * @param {Number} size The initial size
24474      */
24475     setCurrentSize : function(size){
24476         var oldAnimate = this.animate;
24477         this.animate = false;
24478         this.adapter.setElementSize(this, size);
24479         this.animate = oldAnimate;
24480     },
24481     
24482     /**
24483      * Destroy this splitbar. 
24484      * @param {Boolean} removeEl True to remove the element
24485      */
24486     destroy : function(removeEl){
24487         if(this.shim){
24488             this.shim.remove();
24489         }
24490         this.dd.unreg();
24491         this.proxy.parentNode.removeChild(this.proxy);
24492         if(removeEl){
24493             this.el.remove();
24494         }
24495     }
24496 });
24497
24498 /**
24499  * @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.
24500  */
24501 Roo.SplitBar.createProxy = function(dir){
24502     var proxy = new Roo.Element(document.createElement("div"));
24503     proxy.unselectable();
24504     var cls = 'x-splitbar-proxy';
24505     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24506     document.body.appendChild(proxy.dom);
24507     return proxy.dom;
24508 };
24509
24510 /** 
24511  * @class Roo.SplitBar.BasicLayoutAdapter
24512  * Default Adapter. It assumes the splitter and resizing element are not positioned
24513  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24514  */
24515 Roo.SplitBar.BasicLayoutAdapter = function(){
24516 };
24517
24518 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24519     // do nothing for now
24520     init : function(s){
24521     
24522     },
24523     /**
24524      * Called before drag operations to get the current size of the resizing element. 
24525      * @param {Roo.SplitBar} s The SplitBar using this adapter
24526      */
24527      getElementSize : function(s){
24528         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24529             return s.resizingEl.getWidth();
24530         }else{
24531             return s.resizingEl.getHeight();
24532         }
24533     },
24534     
24535     /**
24536      * Called after drag operations to set the size of the resizing element.
24537      * @param {Roo.SplitBar} s The SplitBar using this adapter
24538      * @param {Number} newSize The new size to set
24539      * @param {Function} onComplete A function to be invoked when resizing is complete
24540      */
24541     setElementSize : function(s, newSize, onComplete){
24542         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24543             if(!s.animate){
24544                 s.resizingEl.setWidth(newSize);
24545                 if(onComplete){
24546                     onComplete(s, newSize);
24547                 }
24548             }else{
24549                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24550             }
24551         }else{
24552             
24553             if(!s.animate){
24554                 s.resizingEl.setHeight(newSize);
24555                 if(onComplete){
24556                     onComplete(s, newSize);
24557                 }
24558             }else{
24559                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24560             }
24561         }
24562     }
24563 };
24564
24565 /** 
24566  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24567  * @extends Roo.SplitBar.BasicLayoutAdapter
24568  * Adapter that  moves the splitter element to align with the resized sizing element. 
24569  * Used with an absolute positioned SplitBar.
24570  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24571  * document.body, make sure you assign an id to the body element.
24572  */
24573 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24574     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24575     this.container = Roo.get(container);
24576 };
24577
24578 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24579     init : function(s){
24580         this.basic.init(s);
24581     },
24582     
24583     getElementSize : function(s){
24584         return this.basic.getElementSize(s);
24585     },
24586     
24587     setElementSize : function(s, newSize, onComplete){
24588         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24589     },
24590     
24591     moveSplitter : function(s){
24592         var yes = Roo.SplitBar;
24593         switch(s.placement){
24594             case yes.LEFT:
24595                 s.el.setX(s.resizingEl.getRight());
24596                 break;
24597             case yes.RIGHT:
24598                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24599                 break;
24600             case yes.TOP:
24601                 s.el.setY(s.resizingEl.getBottom());
24602                 break;
24603             case yes.BOTTOM:
24604                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24605                 break;
24606         }
24607     }
24608 };
24609
24610 /**
24611  * Orientation constant - Create a vertical SplitBar
24612  * @static
24613  * @type Number
24614  */
24615 Roo.SplitBar.VERTICAL = 1;
24616
24617 /**
24618  * Orientation constant - Create a horizontal SplitBar
24619  * @static
24620  * @type Number
24621  */
24622 Roo.SplitBar.HORIZONTAL = 2;
24623
24624 /**
24625  * Placement constant - The resizing element is to the left of the splitter element
24626  * @static
24627  * @type Number
24628  */
24629 Roo.SplitBar.LEFT = 1;
24630
24631 /**
24632  * Placement constant - The resizing element is to the right of the splitter element
24633  * @static
24634  * @type Number
24635  */
24636 Roo.SplitBar.RIGHT = 2;
24637
24638 /**
24639  * Placement constant - The resizing element is positioned above the splitter element
24640  * @static
24641  * @type Number
24642  */
24643 Roo.SplitBar.TOP = 3;
24644
24645 /**
24646  * Placement constant - The resizing element is positioned under splitter element
24647  * @static
24648  * @type Number
24649  */
24650 Roo.SplitBar.BOTTOM = 4;
24651 /*
24652  * Based on:
24653  * Ext JS Library 1.1.1
24654  * Copyright(c) 2006-2007, Ext JS, LLC.
24655  *
24656  * Originally Released Under LGPL - original licence link has changed is not relivant.
24657  *
24658  * Fork - LGPL
24659  * <script type="text/javascript">
24660  */
24661
24662 /**
24663  * @class Roo.View
24664  * @extends Roo.util.Observable
24665  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24666  * This class also supports single and multi selection modes. <br>
24667  * Create a data model bound view:
24668  <pre><code>
24669  var store = new Roo.data.Store(...);
24670
24671  var view = new Roo.View({
24672     el : "my-element",
24673     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24674  
24675     singleSelect: true,
24676     selectedClass: "ydataview-selected",
24677     store: store
24678  });
24679
24680  // listen for node click?
24681  view.on("click", function(vw, index, node, e){
24682  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24683  });
24684
24685  // load XML data
24686  dataModel.load("foobar.xml");
24687  </code></pre>
24688  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24689  * <br><br>
24690  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24691  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24692  * 
24693  * Note: old style constructor is still suported (container, template, config)
24694  * 
24695  * @constructor
24696  * Create a new View
24697  * @param {Object} config The config object
24698  * 
24699  */
24700 Roo.View = function(config, depreciated_tpl, depreciated_config){
24701     
24702     if (typeof(depreciated_tpl) == 'undefined') {
24703         // new way.. - universal constructor.
24704         Roo.apply(this, config);
24705         this.el  = Roo.get(this.el);
24706     } else {
24707         // old format..
24708         this.el  = Roo.get(config);
24709         this.tpl = depreciated_tpl;
24710         Roo.apply(this, depreciated_config);
24711     }
24712     this.wrapEl  = this.el.wrap().wrap();
24713     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24714     
24715     
24716     if(typeof(this.tpl) == "string"){
24717         this.tpl = new Roo.Template(this.tpl);
24718     } else {
24719         // support xtype ctors..
24720         this.tpl = new Roo.factory(this.tpl, Roo);
24721     }
24722     
24723     
24724     this.tpl.compile();
24725    
24726   
24727     
24728      
24729     /** @private */
24730     this.addEvents({
24731         /**
24732          * @event beforeclick
24733          * Fires before a click is processed. Returns false to cancel the default action.
24734          * @param {Roo.View} this
24735          * @param {Number} index The index of the target node
24736          * @param {HTMLElement} node The target node
24737          * @param {Roo.EventObject} e The raw event object
24738          */
24739             "beforeclick" : true,
24740         /**
24741          * @event click
24742          * Fires when a template node is clicked.
24743          * @param {Roo.View} this
24744          * @param {Number} index The index of the target node
24745          * @param {HTMLElement} node The target node
24746          * @param {Roo.EventObject} e The raw event object
24747          */
24748             "click" : true,
24749         /**
24750          * @event dblclick
24751          * Fires when a template node is double clicked.
24752          * @param {Roo.View} this
24753          * @param {Number} index The index of the target node
24754          * @param {HTMLElement} node The target node
24755          * @param {Roo.EventObject} e The raw event object
24756          */
24757             "dblclick" : true,
24758         /**
24759          * @event contextmenu
24760          * Fires when a template node is right clicked.
24761          * @param {Roo.View} this
24762          * @param {Number} index The index of the target node
24763          * @param {HTMLElement} node The target node
24764          * @param {Roo.EventObject} e The raw event object
24765          */
24766             "contextmenu" : true,
24767         /**
24768          * @event selectionchange
24769          * Fires when the selected nodes change.
24770          * @param {Roo.View} this
24771          * @param {Array} selections Array of the selected nodes
24772          */
24773             "selectionchange" : true,
24774     
24775         /**
24776          * @event beforeselect
24777          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24778          * @param {Roo.View} this
24779          * @param {HTMLElement} node The node to be selected
24780          * @param {Array} selections Array of currently selected nodes
24781          */
24782             "beforeselect" : true,
24783         /**
24784          * @event preparedata
24785          * Fires on every row to render, to allow you to change the data.
24786          * @param {Roo.View} this
24787          * @param {Object} data to be rendered (change this)
24788          */
24789           "preparedata" : true
24790           
24791           
24792         });
24793
24794
24795
24796     this.el.on({
24797         "click": this.onClick,
24798         "dblclick": this.onDblClick,
24799         "contextmenu": this.onContextMenu,
24800         scope:this
24801     });
24802
24803     this.selections = [];
24804     this.nodes = [];
24805     this.cmp = new Roo.CompositeElementLite([]);
24806     if(this.store){
24807         this.store = Roo.factory(this.store, Roo.data);
24808         this.setStore(this.store, true);
24809     }
24810     
24811     if ( this.footer && this.footer.xtype) {
24812            
24813          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24814         
24815         this.footer.dataSource = this.store
24816         this.footer.container = fctr;
24817         this.footer = Roo.factory(this.footer, Roo);
24818         fctr.insertFirst(this.el);
24819         
24820         // this is a bit insane - as the paging toolbar seems to detach the el..
24821 //        dom.parentNode.parentNode.parentNode
24822          // they get detached?
24823     }
24824     
24825     
24826     Roo.View.superclass.constructor.call(this);
24827     
24828     
24829 };
24830
24831 Roo.extend(Roo.View, Roo.util.Observable, {
24832     
24833      /**
24834      * @cfg {Roo.data.Store} store Data store to load data from.
24835      */
24836     store : false,
24837     
24838     /**
24839      * @cfg {String|Roo.Element} el The container element.
24840      */
24841     el : '',
24842     
24843     /**
24844      * @cfg {String|Roo.Template} tpl The template used by this View 
24845      */
24846     tpl : false,
24847     /**
24848      * @cfg {String} dataName the named area of the template to use as the data area
24849      *                          Works with domtemplates roo-name="name"
24850      */
24851     dataName: false,
24852     /**
24853      * @cfg {String} selectedClass The css class to add to selected nodes
24854      */
24855     selectedClass : "x-view-selected",
24856      /**
24857      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24858      */
24859     emptyText : "",
24860     
24861     /**
24862      * @cfg {String} text to display on mask (default Loading)
24863      */
24864     mask : false,
24865     /**
24866      * @cfg {Boolean} multiSelect Allow multiple selection
24867      */
24868     multiSelect : false,
24869     /**
24870      * @cfg {Boolean} singleSelect Allow single selection
24871      */
24872     singleSelect:  false,
24873     
24874     /**
24875      * @cfg {Boolean} toggleSelect - selecting 
24876      */
24877     toggleSelect : false,
24878     
24879     /**
24880      * Returns the element this view is bound to.
24881      * @return {Roo.Element}
24882      */
24883     getEl : function(){
24884         return this.wrapEl;
24885     },
24886     
24887     
24888
24889     /**
24890      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24891      */
24892     refresh : function(){
24893         var t = this.tpl;
24894         
24895         // if we are using something like 'domtemplate', then
24896         // the what gets used is:
24897         // t.applySubtemplate(NAME, data, wrapping data..)
24898         // the outer template then get' applied with
24899         //     the store 'extra data'
24900         // and the body get's added to the
24901         //      roo-name="data" node?
24902         //      <span class='roo-tpl-{name}'></span> ?????
24903         
24904         
24905         
24906         this.clearSelections();
24907         this.el.update("");
24908         var html = [];
24909         var records = this.store.getRange();
24910         if(records.length < 1) {
24911             
24912             // is this valid??  = should it render a template??
24913             
24914             this.el.update(this.emptyText);
24915             return;
24916         }
24917         var el = this.el;
24918         if (this.dataName) {
24919             this.el.update(t.apply(this.store.meta)); //????
24920             el = this.el.child('.roo-tpl-' + this.dataName);
24921         }
24922         
24923         for(var i = 0, len = records.length; i < len; i++){
24924             var data = this.prepareData(records[i].data, i, records[i]);
24925             this.fireEvent("preparedata", this, data, i, records[i]);
24926             html[html.length] = Roo.util.Format.trim(
24927                 this.dataName ?
24928                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24929                     t.apply(data)
24930             );
24931         }
24932         
24933         
24934         
24935         el.update(html.join(""));
24936         this.nodes = el.dom.childNodes;
24937         this.updateIndexes(0);
24938     },
24939
24940     /**
24941      * Function to override to reformat the data that is sent to
24942      * the template for each node.
24943      * DEPRICATED - use the preparedata event handler.
24944      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24945      * a JSON object for an UpdateManager bound view).
24946      */
24947     prepareData : function(data, index, record)
24948     {
24949         this.fireEvent("preparedata", this, data, index, record);
24950         return data;
24951     },
24952
24953     onUpdate : function(ds, record){
24954         this.clearSelections();
24955         var index = this.store.indexOf(record);
24956         var n = this.nodes[index];
24957         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24958         n.parentNode.removeChild(n);
24959         this.updateIndexes(index, index);
24960     },
24961
24962     
24963     
24964 // --------- FIXME     
24965     onAdd : function(ds, records, index)
24966     {
24967         this.clearSelections();
24968         if(this.nodes.length == 0){
24969             this.refresh();
24970             return;
24971         }
24972         var n = this.nodes[index];
24973         for(var i = 0, len = records.length; i < len; i++){
24974             var d = this.prepareData(records[i].data, i, records[i]);
24975             if(n){
24976                 this.tpl.insertBefore(n, d);
24977             }else{
24978                 
24979                 this.tpl.append(this.el, d);
24980             }
24981         }
24982         this.updateIndexes(index);
24983     },
24984
24985     onRemove : function(ds, record, index){
24986         this.clearSelections();
24987         var el = this.dataName  ?
24988             this.el.child('.roo-tpl-' + this.dataName) :
24989             this.el; 
24990         el.dom.removeChild(this.nodes[index]);
24991         this.updateIndexes(index);
24992     },
24993
24994     /**
24995      * Refresh an individual node.
24996      * @param {Number} index
24997      */
24998     refreshNode : function(index){
24999         this.onUpdate(this.store, this.store.getAt(index));
25000     },
25001
25002     updateIndexes : function(startIndex, endIndex){
25003         var ns = this.nodes;
25004         startIndex = startIndex || 0;
25005         endIndex = endIndex || ns.length - 1;
25006         for(var i = startIndex; i <= endIndex; i++){
25007             ns[i].nodeIndex = i;
25008         }
25009     },
25010
25011     /**
25012      * Changes the data store this view uses and refresh the view.
25013      * @param {Store} store
25014      */
25015     setStore : function(store, initial){
25016         if(!initial && this.store){
25017             this.store.un("datachanged", this.refresh);
25018             this.store.un("add", this.onAdd);
25019             this.store.un("remove", this.onRemove);
25020             this.store.un("update", this.onUpdate);
25021             this.store.un("clear", this.refresh);
25022             this.store.un("beforeload", this.onBeforeLoad);
25023             this.store.un("load", this.onLoad);
25024             this.store.un("loadexception", this.onLoad);
25025         }
25026         if(store){
25027           
25028             store.on("datachanged", this.refresh, this);
25029             store.on("add", this.onAdd, this);
25030             store.on("remove", this.onRemove, this);
25031             store.on("update", this.onUpdate, this);
25032             store.on("clear", this.refresh, this);
25033             store.on("beforeload", this.onBeforeLoad, this);
25034             store.on("load", this.onLoad, this);
25035             store.on("loadexception", this.onLoad, this);
25036         }
25037         
25038         if(store){
25039             this.refresh();
25040         }
25041     },
25042     /**
25043      * onbeforeLoad - masks the loading area.
25044      *
25045      */
25046     onBeforeLoad : function()
25047     {
25048         this.el.update("");
25049         this.el.mask(this.mask ? this.mask : "Loading" ); 
25050     },
25051     onLoad : function ()
25052     {
25053         this.el.unmask();
25054     },
25055     
25056
25057     /**
25058      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25059      * @param {HTMLElement} node
25060      * @return {HTMLElement} The template node
25061      */
25062     findItemFromChild : function(node){
25063         var el = this.dataName  ?
25064             this.el.child('.roo-tpl-' + this.dataName,true) :
25065             this.el.dom; 
25066         
25067         if(!node || node.parentNode == el){
25068                     return node;
25069             }
25070             var p = node.parentNode;
25071             while(p && p != el){
25072             if(p.parentNode == el){
25073                 return p;
25074             }
25075             p = p.parentNode;
25076         }
25077             return null;
25078     },
25079
25080     /** @ignore */
25081     onClick : function(e){
25082         var item = this.findItemFromChild(e.getTarget());
25083         if(item){
25084             var index = this.indexOf(item);
25085             if(this.onItemClick(item, index, e) !== false){
25086                 this.fireEvent("click", this, index, item, e);
25087             }
25088         }else{
25089             this.clearSelections();
25090         }
25091     },
25092
25093     /** @ignore */
25094     onContextMenu : function(e){
25095         var item = this.findItemFromChild(e.getTarget());
25096         if(item){
25097             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25098         }
25099     },
25100
25101     /** @ignore */
25102     onDblClick : function(e){
25103         var item = this.findItemFromChild(e.getTarget());
25104         if(item){
25105             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25106         }
25107     },
25108
25109     onItemClick : function(item, index, e)
25110     {
25111         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25112             return false;
25113         }
25114         if (this.toggleSelect) {
25115             var m = this.isSelected(item) ? 'unselect' : 'select';
25116             Roo.log(m);
25117             var _t = this;
25118             _t[m](item, true, false);
25119             return true;
25120         }
25121         if(this.multiSelect || this.singleSelect){
25122             if(this.multiSelect && e.shiftKey && this.lastSelection){
25123                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25124             }else{
25125                 this.select(item, this.multiSelect && e.ctrlKey);
25126                 this.lastSelection = item;
25127             }
25128             e.preventDefault();
25129         }
25130         return true;
25131     },
25132
25133     /**
25134      * Get the number of selected nodes.
25135      * @return {Number}
25136      */
25137     getSelectionCount : function(){
25138         return this.selections.length;
25139     },
25140
25141     /**
25142      * Get the currently selected nodes.
25143      * @return {Array} An array of HTMLElements
25144      */
25145     getSelectedNodes : function(){
25146         return this.selections;
25147     },
25148
25149     /**
25150      * Get the indexes of the selected nodes.
25151      * @return {Array}
25152      */
25153     getSelectedIndexes : function(){
25154         var indexes = [], s = this.selections;
25155         for(var i = 0, len = s.length; i < len; i++){
25156             indexes.push(s[i].nodeIndex);
25157         }
25158         return indexes;
25159     },
25160
25161     /**
25162      * Clear all selections
25163      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25164      */
25165     clearSelections : function(suppressEvent){
25166         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25167             this.cmp.elements = this.selections;
25168             this.cmp.removeClass(this.selectedClass);
25169             this.selections = [];
25170             if(!suppressEvent){
25171                 this.fireEvent("selectionchange", this, this.selections);
25172             }
25173         }
25174     },
25175
25176     /**
25177      * Returns true if the passed node is selected
25178      * @param {HTMLElement/Number} node The node or node index
25179      * @return {Boolean}
25180      */
25181     isSelected : function(node){
25182         var s = this.selections;
25183         if(s.length < 1){
25184             return false;
25185         }
25186         node = this.getNode(node);
25187         return s.indexOf(node) !== -1;
25188     },
25189
25190     /**
25191      * Selects nodes.
25192      * @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
25193      * @param {Boolean} keepExisting (optional) true to keep existing selections
25194      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25195      */
25196     select : function(nodeInfo, keepExisting, suppressEvent){
25197         if(nodeInfo instanceof Array){
25198             if(!keepExisting){
25199                 this.clearSelections(true);
25200             }
25201             for(var i = 0, len = nodeInfo.length; i < len; i++){
25202                 this.select(nodeInfo[i], true, true);
25203             }
25204             return;
25205         } 
25206         var node = this.getNode(nodeInfo);
25207         if(!node || this.isSelected(node)){
25208             return; // already selected.
25209         }
25210         if(!keepExisting){
25211             this.clearSelections(true);
25212         }
25213         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25214             Roo.fly(node).addClass(this.selectedClass);
25215             this.selections.push(node);
25216             if(!suppressEvent){
25217                 this.fireEvent("selectionchange", this, this.selections);
25218             }
25219         }
25220         
25221         
25222     },
25223       /**
25224      * Unselects nodes.
25225      * @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
25226      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25227      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25228      */
25229     unselect : function(nodeInfo, keepExisting, suppressEvent)
25230     {
25231         if(nodeInfo instanceof Array){
25232             Roo.each(this.selections, function(s) {
25233                 this.unselect(s, nodeInfo);
25234             }, this);
25235             return;
25236         }
25237         var node = this.getNode(nodeInfo);
25238         if(!node || !this.isSelected(node)){
25239             Roo.log("not selected");
25240             return; // not selected.
25241         }
25242         // fireevent???
25243         var ns = [];
25244         Roo.each(this.selections, function(s) {
25245             if (s == node ) {
25246                 Roo.fly(node).removeClass(this.selectedClass);
25247
25248                 return;
25249             }
25250             ns.push(s);
25251         },this);
25252         
25253         this.selections= ns;
25254         this.fireEvent("selectionchange", this, this.selections);
25255     },
25256
25257     /**
25258      * Gets a template node.
25259      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25260      * @return {HTMLElement} The node or null if it wasn't found
25261      */
25262     getNode : function(nodeInfo){
25263         if(typeof nodeInfo == "string"){
25264             return document.getElementById(nodeInfo);
25265         }else if(typeof nodeInfo == "number"){
25266             return this.nodes[nodeInfo];
25267         }
25268         return nodeInfo;
25269     },
25270
25271     /**
25272      * Gets a range template nodes.
25273      * @param {Number} startIndex
25274      * @param {Number} endIndex
25275      * @return {Array} An array of nodes
25276      */
25277     getNodes : function(start, end){
25278         var ns = this.nodes;
25279         start = start || 0;
25280         end = typeof end == "undefined" ? ns.length - 1 : end;
25281         var nodes = [];
25282         if(start <= end){
25283             for(var i = start; i <= end; i++){
25284                 nodes.push(ns[i]);
25285             }
25286         } else{
25287             for(var i = start; i >= end; i--){
25288                 nodes.push(ns[i]);
25289             }
25290         }
25291         return nodes;
25292     },
25293
25294     /**
25295      * Finds the index of the passed node
25296      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25297      * @return {Number} The index of the node or -1
25298      */
25299     indexOf : function(node){
25300         node = this.getNode(node);
25301         if(typeof node.nodeIndex == "number"){
25302             return node.nodeIndex;
25303         }
25304         var ns = this.nodes;
25305         for(var i = 0, len = ns.length; i < len; i++){
25306             if(ns[i] == node){
25307                 return i;
25308             }
25309         }
25310         return -1;
25311     }
25312 });
25313 /*
25314  * Based on:
25315  * Ext JS Library 1.1.1
25316  * Copyright(c) 2006-2007, Ext JS, LLC.
25317  *
25318  * Originally Released Under LGPL - original licence link has changed is not relivant.
25319  *
25320  * Fork - LGPL
25321  * <script type="text/javascript">
25322  */
25323
25324 /**
25325  * @class Roo.JsonView
25326  * @extends Roo.View
25327  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25328 <pre><code>
25329 var view = new Roo.JsonView({
25330     container: "my-element",
25331     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25332     multiSelect: true, 
25333     jsonRoot: "data" 
25334 });
25335
25336 // listen for node click?
25337 view.on("click", function(vw, index, node, e){
25338     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25339 });
25340
25341 // direct load of JSON data
25342 view.load("foobar.php");
25343
25344 // Example from my blog list
25345 var tpl = new Roo.Template(
25346     '&lt;div class="entry"&gt;' +
25347     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25348     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25349     "&lt;/div&gt;&lt;hr /&gt;"
25350 );
25351
25352 var moreView = new Roo.JsonView({
25353     container :  "entry-list", 
25354     template : tpl,
25355     jsonRoot: "posts"
25356 });
25357 moreView.on("beforerender", this.sortEntries, this);
25358 moreView.load({
25359     url: "/blog/get-posts.php",
25360     params: "allposts=true",
25361     text: "Loading Blog Entries..."
25362 });
25363 </code></pre>
25364
25365 * Note: old code is supported with arguments : (container, template, config)
25366
25367
25368  * @constructor
25369  * Create a new JsonView
25370  * 
25371  * @param {Object} config The config object
25372  * 
25373  */
25374 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25375     
25376     
25377     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25378
25379     var um = this.el.getUpdateManager();
25380     um.setRenderer(this);
25381     um.on("update", this.onLoad, this);
25382     um.on("failure", this.onLoadException, this);
25383
25384     /**
25385      * @event beforerender
25386      * Fires before rendering of the downloaded JSON data.
25387      * @param {Roo.JsonView} this
25388      * @param {Object} data The JSON data loaded
25389      */
25390     /**
25391      * @event load
25392      * Fires when data is loaded.
25393      * @param {Roo.JsonView} this
25394      * @param {Object} data The JSON data loaded
25395      * @param {Object} response The raw Connect response object
25396      */
25397     /**
25398      * @event loadexception
25399      * Fires when loading fails.
25400      * @param {Roo.JsonView} this
25401      * @param {Object} response The raw Connect response object
25402      */
25403     this.addEvents({
25404         'beforerender' : true,
25405         'load' : true,
25406         'loadexception' : true
25407     });
25408 };
25409 Roo.extend(Roo.JsonView, Roo.View, {
25410     /**
25411      * @type {String} The root property in the loaded JSON object that contains the data
25412      */
25413     jsonRoot : "",
25414
25415     /**
25416      * Refreshes the view.
25417      */
25418     refresh : function(){
25419         this.clearSelections();
25420         this.el.update("");
25421         var html = [];
25422         var o = this.jsonData;
25423         if(o && o.length > 0){
25424             for(var i = 0, len = o.length; i < len; i++){
25425                 var data = this.prepareData(o[i], i, o);
25426                 html[html.length] = this.tpl.apply(data);
25427             }
25428         }else{
25429             html.push(this.emptyText);
25430         }
25431         this.el.update(html.join(""));
25432         this.nodes = this.el.dom.childNodes;
25433         this.updateIndexes(0);
25434     },
25435
25436     /**
25437      * 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.
25438      * @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:
25439      <pre><code>
25440      view.load({
25441          url: "your-url.php",
25442          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25443          callback: yourFunction,
25444          scope: yourObject, //(optional scope)
25445          discardUrl: false,
25446          nocache: false,
25447          text: "Loading...",
25448          timeout: 30,
25449          scripts: false
25450      });
25451      </code></pre>
25452      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25453      * 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.
25454      * @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}
25455      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25456      * @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.
25457      */
25458     load : function(){
25459         var um = this.el.getUpdateManager();
25460         um.update.apply(um, arguments);
25461     },
25462
25463     render : function(el, response){
25464         this.clearSelections();
25465         this.el.update("");
25466         var o;
25467         try{
25468             o = Roo.util.JSON.decode(response.responseText);
25469             if(this.jsonRoot){
25470                 
25471                 o = o[this.jsonRoot];
25472             }
25473         } catch(e){
25474         }
25475         /**
25476          * The current JSON data or null
25477          */
25478         this.jsonData = o;
25479         this.beforeRender();
25480         this.refresh();
25481     },
25482
25483 /**
25484  * Get the number of records in the current JSON dataset
25485  * @return {Number}
25486  */
25487     getCount : function(){
25488         return this.jsonData ? this.jsonData.length : 0;
25489     },
25490
25491 /**
25492  * Returns the JSON object for the specified node(s)
25493  * @param {HTMLElement/Array} node The node or an array of nodes
25494  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25495  * you get the JSON object for the node
25496  */
25497     getNodeData : function(node){
25498         if(node instanceof Array){
25499             var data = [];
25500             for(var i = 0, len = node.length; i < len; i++){
25501                 data.push(this.getNodeData(node[i]));
25502             }
25503             return data;
25504         }
25505         return this.jsonData[this.indexOf(node)] || null;
25506     },
25507
25508     beforeRender : function(){
25509         this.snapshot = this.jsonData;
25510         if(this.sortInfo){
25511             this.sort.apply(this, this.sortInfo);
25512         }
25513         this.fireEvent("beforerender", this, this.jsonData);
25514     },
25515
25516     onLoad : function(el, o){
25517         this.fireEvent("load", this, this.jsonData, o);
25518     },
25519
25520     onLoadException : function(el, o){
25521         this.fireEvent("loadexception", this, o);
25522     },
25523
25524 /**
25525  * Filter the data by a specific property.
25526  * @param {String} property A property on your JSON objects
25527  * @param {String/RegExp} value Either string that the property values
25528  * should start with, or a RegExp to test against the property
25529  */
25530     filter : function(property, value){
25531         if(this.jsonData){
25532             var data = [];
25533             var ss = this.snapshot;
25534             if(typeof value == "string"){
25535                 var vlen = value.length;
25536                 if(vlen == 0){
25537                     this.clearFilter();
25538                     return;
25539                 }
25540                 value = value.toLowerCase();
25541                 for(var i = 0, len = ss.length; i < len; i++){
25542                     var o = ss[i];
25543                     if(o[property].substr(0, vlen).toLowerCase() == value){
25544                         data.push(o);
25545                     }
25546                 }
25547             } else if(value.exec){ // regex?
25548                 for(var i = 0, len = ss.length; i < len; i++){
25549                     var o = ss[i];
25550                     if(value.test(o[property])){
25551                         data.push(o);
25552                     }
25553                 }
25554             } else{
25555                 return;
25556             }
25557             this.jsonData = data;
25558             this.refresh();
25559         }
25560     },
25561
25562 /**
25563  * Filter by a function. The passed function will be called with each
25564  * object in the current dataset. If the function returns true the value is kept,
25565  * otherwise it is filtered.
25566  * @param {Function} fn
25567  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25568  */
25569     filterBy : function(fn, scope){
25570         if(this.jsonData){
25571             var data = [];
25572             var ss = this.snapshot;
25573             for(var i = 0, len = ss.length; i < len; i++){
25574                 var o = ss[i];
25575                 if(fn.call(scope || this, o)){
25576                     data.push(o);
25577                 }
25578             }
25579             this.jsonData = data;
25580             this.refresh();
25581         }
25582     },
25583
25584 /**
25585  * Clears the current filter.
25586  */
25587     clearFilter : function(){
25588         if(this.snapshot && this.jsonData != this.snapshot){
25589             this.jsonData = this.snapshot;
25590             this.refresh();
25591         }
25592     },
25593
25594
25595 /**
25596  * Sorts the data for this view and refreshes it.
25597  * @param {String} property A property on your JSON objects to sort on
25598  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25599  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25600  */
25601     sort : function(property, dir, sortType){
25602         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25603         if(this.jsonData){
25604             var p = property;
25605             var dsc = dir && dir.toLowerCase() == "desc";
25606             var f = function(o1, o2){
25607                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25608                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25609                 ;
25610                 if(v1 < v2){
25611                     return dsc ? +1 : -1;
25612                 } else if(v1 > v2){
25613                     return dsc ? -1 : +1;
25614                 } else{
25615                     return 0;
25616                 }
25617             };
25618             this.jsonData.sort(f);
25619             this.refresh();
25620             if(this.jsonData != this.snapshot){
25621                 this.snapshot.sort(f);
25622             }
25623         }
25624     }
25625 });/*
25626  * Based on:
25627  * Ext JS Library 1.1.1
25628  * Copyright(c) 2006-2007, Ext JS, LLC.
25629  *
25630  * Originally Released Under LGPL - original licence link has changed is not relivant.
25631  *
25632  * Fork - LGPL
25633  * <script type="text/javascript">
25634  */
25635  
25636
25637 /**
25638  * @class Roo.ColorPalette
25639  * @extends Roo.Component
25640  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25641  * Here's an example of typical usage:
25642  * <pre><code>
25643 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25644 cp.render('my-div');
25645
25646 cp.on('select', function(palette, selColor){
25647     // do something with selColor
25648 });
25649 </code></pre>
25650  * @constructor
25651  * Create a new ColorPalette
25652  * @param {Object} config The config object
25653  */
25654 Roo.ColorPalette = function(config){
25655     Roo.ColorPalette.superclass.constructor.call(this, config);
25656     this.addEvents({
25657         /**
25658              * @event select
25659              * Fires when a color is selected
25660              * @param {ColorPalette} this
25661              * @param {String} color The 6-digit color hex code (without the # symbol)
25662              */
25663         select: true
25664     });
25665
25666     if(this.handler){
25667         this.on("select", this.handler, this.scope, true);
25668     }
25669 };
25670 Roo.extend(Roo.ColorPalette, Roo.Component, {
25671     /**
25672      * @cfg {String} itemCls
25673      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25674      */
25675     itemCls : "x-color-palette",
25676     /**
25677      * @cfg {String} value
25678      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25679      * the hex codes are case-sensitive.
25680      */
25681     value : null,
25682     clickEvent:'click',
25683     // private
25684     ctype: "Roo.ColorPalette",
25685
25686     /**
25687      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25688      */
25689     allowReselect : false,
25690
25691     /**
25692      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25693      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25694      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25695      * of colors with the width setting until the box is symmetrical.</p>
25696      * <p>You can override individual colors if needed:</p>
25697      * <pre><code>
25698 var cp = new Roo.ColorPalette();
25699 cp.colors[0] = "FF0000";  // change the first box to red
25700 </code></pre>
25701
25702 Or you can provide a custom array of your own for complete control:
25703 <pre><code>
25704 var cp = new Roo.ColorPalette();
25705 cp.colors = ["000000", "993300", "333300"];
25706 </code></pre>
25707      * @type Array
25708      */
25709     colors : [
25710         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25711         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25712         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25713         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25714         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25715     ],
25716
25717     // private
25718     onRender : function(container, position){
25719         var t = new Roo.MasterTemplate(
25720             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25721         );
25722         var c = this.colors;
25723         for(var i = 0, len = c.length; i < len; i++){
25724             t.add([c[i]]);
25725         }
25726         var el = document.createElement("div");
25727         el.className = this.itemCls;
25728         t.overwrite(el);
25729         container.dom.insertBefore(el, position);
25730         this.el = Roo.get(el);
25731         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25732         if(this.clickEvent != 'click'){
25733             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25734         }
25735     },
25736
25737     // private
25738     afterRender : function(){
25739         Roo.ColorPalette.superclass.afterRender.call(this);
25740         if(this.value){
25741             var s = this.value;
25742             this.value = null;
25743             this.select(s);
25744         }
25745     },
25746
25747     // private
25748     handleClick : function(e, t){
25749         e.preventDefault();
25750         if(!this.disabled){
25751             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25752             this.select(c.toUpperCase());
25753         }
25754     },
25755
25756     /**
25757      * Selects the specified color in the palette (fires the select event)
25758      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25759      */
25760     select : function(color){
25761         color = color.replace("#", "");
25762         if(color != this.value || this.allowReselect){
25763             var el = this.el;
25764             if(this.value){
25765                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25766             }
25767             el.child("a.color-"+color).addClass("x-color-palette-sel");
25768             this.value = color;
25769             this.fireEvent("select", this, color);
25770         }
25771     }
25772 });/*
25773  * Based on:
25774  * Ext JS Library 1.1.1
25775  * Copyright(c) 2006-2007, Ext JS, LLC.
25776  *
25777  * Originally Released Under LGPL - original licence link has changed is not relivant.
25778  *
25779  * Fork - LGPL
25780  * <script type="text/javascript">
25781  */
25782  
25783 /**
25784  * @class Roo.DatePicker
25785  * @extends Roo.Component
25786  * Simple date picker class.
25787  * @constructor
25788  * Create a new DatePicker
25789  * @param {Object} config The config object
25790  */
25791 Roo.DatePicker = function(config){
25792     Roo.DatePicker.superclass.constructor.call(this, config);
25793
25794     this.value = config && config.value ?
25795                  config.value.clearTime() : new Date().clearTime();
25796
25797     this.addEvents({
25798         /**
25799              * @event select
25800              * Fires when a date is selected
25801              * @param {DatePicker} this
25802              * @param {Date} date The selected date
25803              */
25804         'select': true,
25805         /**
25806              * @event monthchange
25807              * Fires when the displayed month changes 
25808              * @param {DatePicker} this
25809              * @param {Date} date The selected month
25810              */
25811         'monthchange': true
25812     });
25813
25814     if(this.handler){
25815         this.on("select", this.handler,  this.scope || this);
25816     }
25817     // build the disabledDatesRE
25818     if(!this.disabledDatesRE && this.disabledDates){
25819         var dd = this.disabledDates;
25820         var re = "(?:";
25821         for(var i = 0; i < dd.length; i++){
25822             re += dd[i];
25823             if(i != dd.length-1) re += "|";
25824         }
25825         this.disabledDatesRE = new RegExp(re + ")");
25826     }
25827 };
25828
25829 Roo.extend(Roo.DatePicker, Roo.Component, {
25830     /**
25831      * @cfg {String} todayText
25832      * The text to display on the button that selects the current date (defaults to "Today")
25833      */
25834     todayText : "Today",
25835     /**
25836      * @cfg {String} okText
25837      * The text to display on the ok button
25838      */
25839     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25840     /**
25841      * @cfg {String} cancelText
25842      * The text to display on the cancel button
25843      */
25844     cancelText : "Cancel",
25845     /**
25846      * @cfg {String} todayTip
25847      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25848      */
25849     todayTip : "{0} (Spacebar)",
25850     /**
25851      * @cfg {Date} minDate
25852      * Minimum allowable date (JavaScript date object, defaults to null)
25853      */
25854     minDate : null,
25855     /**
25856      * @cfg {Date} maxDate
25857      * Maximum allowable date (JavaScript date object, defaults to null)
25858      */
25859     maxDate : null,
25860     /**
25861      * @cfg {String} minText
25862      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25863      */
25864     minText : "This date is before the minimum date",
25865     /**
25866      * @cfg {String} maxText
25867      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25868      */
25869     maxText : "This date is after the maximum date",
25870     /**
25871      * @cfg {String} format
25872      * The default date format string which can be overriden for localization support.  The format must be
25873      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25874      */
25875     format : "m/d/y",
25876     /**
25877      * @cfg {Array} disabledDays
25878      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25879      */
25880     disabledDays : null,
25881     /**
25882      * @cfg {String} disabledDaysText
25883      * The tooltip to display when the date falls on a disabled day (defaults to "")
25884      */
25885     disabledDaysText : "",
25886     /**
25887      * @cfg {RegExp} disabledDatesRE
25888      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25889      */
25890     disabledDatesRE : null,
25891     /**
25892      * @cfg {String} disabledDatesText
25893      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25894      */
25895     disabledDatesText : "",
25896     /**
25897      * @cfg {Boolean} constrainToViewport
25898      * True to constrain the date picker to the viewport (defaults to true)
25899      */
25900     constrainToViewport : true,
25901     /**
25902      * @cfg {Array} monthNames
25903      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25904      */
25905     monthNames : Date.monthNames,
25906     /**
25907      * @cfg {Array} dayNames
25908      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25909      */
25910     dayNames : Date.dayNames,
25911     /**
25912      * @cfg {String} nextText
25913      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25914      */
25915     nextText: 'Next Month (Control+Right)',
25916     /**
25917      * @cfg {String} prevText
25918      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25919      */
25920     prevText: 'Previous Month (Control+Left)',
25921     /**
25922      * @cfg {String} monthYearText
25923      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25924      */
25925     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25926     /**
25927      * @cfg {Number} startDay
25928      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25929      */
25930     startDay : 0,
25931     /**
25932      * @cfg {Bool} showClear
25933      * Show a clear button (usefull for date form elements that can be blank.)
25934      */
25935     
25936     showClear: false,
25937     
25938     /**
25939      * Sets the value of the date field
25940      * @param {Date} value The date to set
25941      */
25942     setValue : function(value){
25943         var old = this.value;
25944         
25945         if (typeof(value) == 'string') {
25946          
25947             value = Date.parseDate(value, this.format);
25948         }
25949         if (!value) {
25950             value = new Date();
25951         }
25952         
25953         this.value = value.clearTime(true);
25954         if(this.el){
25955             this.update(this.value);
25956         }
25957     },
25958
25959     /**
25960      * Gets the current selected value of the date field
25961      * @return {Date} The selected date
25962      */
25963     getValue : function(){
25964         return this.value;
25965     },
25966
25967     // private
25968     focus : function(){
25969         if(this.el){
25970             this.update(this.activeDate);
25971         }
25972     },
25973
25974     // privateval
25975     onRender : function(container, position){
25976         
25977         var m = [
25978              '<table cellspacing="0">',
25979                 '<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>',
25980                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25981         var dn = this.dayNames;
25982         for(var i = 0; i < 7; i++){
25983             var d = this.startDay+i;
25984             if(d > 6){
25985                 d = d-7;
25986             }
25987             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25988         }
25989         m[m.length] = "</tr></thead><tbody><tr>";
25990         for(var i = 0; i < 42; i++) {
25991             if(i % 7 == 0 && i != 0){
25992                 m[m.length] = "</tr><tr>";
25993             }
25994             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25995         }
25996         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25997             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25998
25999         var el = document.createElement("div");
26000         el.className = "x-date-picker";
26001         el.innerHTML = m.join("");
26002
26003         container.dom.insertBefore(el, position);
26004
26005         this.el = Roo.get(el);
26006         this.eventEl = Roo.get(el.firstChild);
26007
26008         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26009             handler: this.showPrevMonth,
26010             scope: this,
26011             preventDefault:true,
26012             stopDefault:true
26013         });
26014
26015         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26016             handler: this.showNextMonth,
26017             scope: this,
26018             preventDefault:true,
26019             stopDefault:true
26020         });
26021
26022         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26023
26024         this.monthPicker = this.el.down('div.x-date-mp');
26025         this.monthPicker.enableDisplayMode('block');
26026         
26027         var kn = new Roo.KeyNav(this.eventEl, {
26028             "left" : function(e){
26029                 e.ctrlKey ?
26030                     this.showPrevMonth() :
26031                     this.update(this.activeDate.add("d", -1));
26032             },
26033
26034             "right" : function(e){
26035                 e.ctrlKey ?
26036                     this.showNextMonth() :
26037                     this.update(this.activeDate.add("d", 1));
26038             },
26039
26040             "up" : function(e){
26041                 e.ctrlKey ?
26042                     this.showNextYear() :
26043                     this.update(this.activeDate.add("d", -7));
26044             },
26045
26046             "down" : function(e){
26047                 e.ctrlKey ?
26048                     this.showPrevYear() :
26049                     this.update(this.activeDate.add("d", 7));
26050             },
26051
26052             "pageUp" : function(e){
26053                 this.showNextMonth();
26054             },
26055
26056             "pageDown" : function(e){
26057                 this.showPrevMonth();
26058             },
26059
26060             "enter" : function(e){
26061                 e.stopPropagation();
26062                 return true;
26063             },
26064
26065             scope : this
26066         });
26067
26068         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26069
26070         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26071
26072         this.el.unselectable();
26073         
26074         this.cells = this.el.select("table.x-date-inner tbody td");
26075         this.textNodes = this.el.query("table.x-date-inner tbody span");
26076
26077         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26078             text: "&#160;",
26079             tooltip: this.monthYearText
26080         });
26081
26082         this.mbtn.on('click', this.showMonthPicker, this);
26083         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26084
26085
26086         var today = (new Date()).dateFormat(this.format);
26087         
26088         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26089         if (this.showClear) {
26090             baseTb.add( new Roo.Toolbar.Fill());
26091         }
26092         baseTb.add({
26093             text: String.format(this.todayText, today),
26094             tooltip: String.format(this.todayTip, today),
26095             handler: this.selectToday,
26096             scope: this
26097         });
26098         
26099         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26100             
26101         //});
26102         if (this.showClear) {
26103             
26104             baseTb.add( new Roo.Toolbar.Fill());
26105             baseTb.add({
26106                 text: '&#160;',
26107                 cls: 'x-btn-icon x-btn-clear',
26108                 handler: function() {
26109                     //this.value = '';
26110                     this.fireEvent("select", this, '');
26111                 },
26112                 scope: this
26113             });
26114         }
26115         
26116         
26117         if(Roo.isIE){
26118             this.el.repaint();
26119         }
26120         this.update(this.value);
26121     },
26122
26123     createMonthPicker : function(){
26124         if(!this.monthPicker.dom.firstChild){
26125             var buf = ['<table border="0" cellspacing="0">'];
26126             for(var i = 0; i < 6; i++){
26127                 buf.push(
26128                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26129                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26130                     i == 0 ?
26131                     '<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>' :
26132                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26133                 );
26134             }
26135             buf.push(
26136                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26137                     this.okText,
26138                     '</button><button type="button" class="x-date-mp-cancel">',
26139                     this.cancelText,
26140                     '</button></td></tr>',
26141                 '</table>'
26142             );
26143             this.monthPicker.update(buf.join(''));
26144             this.monthPicker.on('click', this.onMonthClick, this);
26145             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26146
26147             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26148             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26149
26150             this.mpMonths.each(function(m, a, i){
26151                 i += 1;
26152                 if((i%2) == 0){
26153                     m.dom.xmonth = 5 + Math.round(i * .5);
26154                 }else{
26155                     m.dom.xmonth = Math.round((i-1) * .5);
26156                 }
26157             });
26158         }
26159     },
26160
26161     showMonthPicker : function(){
26162         this.createMonthPicker();
26163         var size = this.el.getSize();
26164         this.monthPicker.setSize(size);
26165         this.monthPicker.child('table').setSize(size);
26166
26167         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26168         this.updateMPMonth(this.mpSelMonth);
26169         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26170         this.updateMPYear(this.mpSelYear);
26171
26172         this.monthPicker.slideIn('t', {duration:.2});
26173     },
26174
26175     updateMPYear : function(y){
26176         this.mpyear = y;
26177         var ys = this.mpYears.elements;
26178         for(var i = 1; i <= 10; i++){
26179             var td = ys[i-1], y2;
26180             if((i%2) == 0){
26181                 y2 = y + Math.round(i * .5);
26182                 td.firstChild.innerHTML = y2;
26183                 td.xyear = y2;
26184             }else{
26185                 y2 = y - (5-Math.round(i * .5));
26186                 td.firstChild.innerHTML = y2;
26187                 td.xyear = y2;
26188             }
26189             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26190         }
26191     },
26192
26193     updateMPMonth : function(sm){
26194         this.mpMonths.each(function(m, a, i){
26195             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26196         });
26197     },
26198
26199     selectMPMonth: function(m){
26200         
26201     },
26202
26203     onMonthClick : function(e, t){
26204         e.stopEvent();
26205         var el = new Roo.Element(t), pn;
26206         if(el.is('button.x-date-mp-cancel')){
26207             this.hideMonthPicker();
26208         }
26209         else if(el.is('button.x-date-mp-ok')){
26210             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26211             this.hideMonthPicker();
26212         }
26213         else if(pn = el.up('td.x-date-mp-month', 2)){
26214             this.mpMonths.removeClass('x-date-mp-sel');
26215             pn.addClass('x-date-mp-sel');
26216             this.mpSelMonth = pn.dom.xmonth;
26217         }
26218         else if(pn = el.up('td.x-date-mp-year', 2)){
26219             this.mpYears.removeClass('x-date-mp-sel');
26220             pn.addClass('x-date-mp-sel');
26221             this.mpSelYear = pn.dom.xyear;
26222         }
26223         else if(el.is('a.x-date-mp-prev')){
26224             this.updateMPYear(this.mpyear-10);
26225         }
26226         else if(el.is('a.x-date-mp-next')){
26227             this.updateMPYear(this.mpyear+10);
26228         }
26229     },
26230
26231     onMonthDblClick : function(e, t){
26232         e.stopEvent();
26233         var el = new Roo.Element(t), pn;
26234         if(pn = el.up('td.x-date-mp-month', 2)){
26235             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26236             this.hideMonthPicker();
26237         }
26238         else if(pn = el.up('td.x-date-mp-year', 2)){
26239             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26240             this.hideMonthPicker();
26241         }
26242     },
26243
26244     hideMonthPicker : function(disableAnim){
26245         if(this.monthPicker){
26246             if(disableAnim === true){
26247                 this.monthPicker.hide();
26248             }else{
26249                 this.monthPicker.slideOut('t', {duration:.2});
26250             }
26251         }
26252     },
26253
26254     // private
26255     showPrevMonth : function(e){
26256         this.update(this.activeDate.add("mo", -1));
26257     },
26258
26259     // private
26260     showNextMonth : function(e){
26261         this.update(this.activeDate.add("mo", 1));
26262     },
26263
26264     // private
26265     showPrevYear : function(){
26266         this.update(this.activeDate.add("y", -1));
26267     },
26268
26269     // private
26270     showNextYear : function(){
26271         this.update(this.activeDate.add("y", 1));
26272     },
26273
26274     // private
26275     handleMouseWheel : function(e){
26276         var delta = e.getWheelDelta();
26277         if(delta > 0){
26278             this.showPrevMonth();
26279             e.stopEvent();
26280         } else if(delta < 0){
26281             this.showNextMonth();
26282             e.stopEvent();
26283         }
26284     },
26285
26286     // private
26287     handleDateClick : function(e, t){
26288         e.stopEvent();
26289         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26290             this.setValue(new Date(t.dateValue));
26291             this.fireEvent("select", this, this.value);
26292         }
26293     },
26294
26295     // private
26296     selectToday : function(){
26297         this.setValue(new Date().clearTime());
26298         this.fireEvent("select", this, this.value);
26299     },
26300
26301     // private
26302     update : function(date)
26303     {
26304         var vd = this.activeDate;
26305         this.activeDate = date;
26306         if(vd && this.el){
26307             var t = date.getTime();
26308             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26309                 this.cells.removeClass("x-date-selected");
26310                 this.cells.each(function(c){
26311                    if(c.dom.firstChild.dateValue == t){
26312                        c.addClass("x-date-selected");
26313                        setTimeout(function(){
26314                             try{c.dom.firstChild.focus();}catch(e){}
26315                        }, 50);
26316                        return false;
26317                    }
26318                 });
26319                 return;
26320             }
26321         }
26322         
26323         var days = date.getDaysInMonth();
26324         var firstOfMonth = date.getFirstDateOfMonth();
26325         var startingPos = firstOfMonth.getDay()-this.startDay;
26326
26327         if(startingPos <= this.startDay){
26328             startingPos += 7;
26329         }
26330
26331         var pm = date.add("mo", -1);
26332         var prevStart = pm.getDaysInMonth()-startingPos;
26333
26334         var cells = this.cells.elements;
26335         var textEls = this.textNodes;
26336         days += startingPos;
26337
26338         // convert everything to numbers so it's fast
26339         var day = 86400000;
26340         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26341         var today = new Date().clearTime().getTime();
26342         var sel = date.clearTime().getTime();
26343         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26344         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26345         var ddMatch = this.disabledDatesRE;
26346         var ddText = this.disabledDatesText;
26347         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26348         var ddaysText = this.disabledDaysText;
26349         var format = this.format;
26350
26351         var setCellClass = function(cal, cell){
26352             cell.title = "";
26353             var t = d.getTime();
26354             cell.firstChild.dateValue = t;
26355             if(t == today){
26356                 cell.className += " x-date-today";
26357                 cell.title = cal.todayText;
26358             }
26359             if(t == sel){
26360                 cell.className += " x-date-selected";
26361                 setTimeout(function(){
26362                     try{cell.firstChild.focus();}catch(e){}
26363                 }, 50);
26364             }
26365             // disabling
26366             if(t < min) {
26367                 cell.className = " x-date-disabled";
26368                 cell.title = cal.minText;
26369                 return;
26370             }
26371             if(t > max) {
26372                 cell.className = " x-date-disabled";
26373                 cell.title = cal.maxText;
26374                 return;
26375             }
26376             if(ddays){
26377                 if(ddays.indexOf(d.getDay()) != -1){
26378                     cell.title = ddaysText;
26379                     cell.className = " x-date-disabled";
26380                 }
26381             }
26382             if(ddMatch && format){
26383                 var fvalue = d.dateFormat(format);
26384                 if(ddMatch.test(fvalue)){
26385                     cell.title = ddText.replace("%0", fvalue);
26386                     cell.className = " x-date-disabled";
26387                 }
26388             }
26389         };
26390
26391         var i = 0;
26392         for(; i < startingPos; i++) {
26393             textEls[i].innerHTML = (++prevStart);
26394             d.setDate(d.getDate()+1);
26395             cells[i].className = "x-date-prevday";
26396             setCellClass(this, cells[i]);
26397         }
26398         for(; i < days; i++){
26399             intDay = i - startingPos + 1;
26400             textEls[i].innerHTML = (intDay);
26401             d.setDate(d.getDate()+1);
26402             cells[i].className = "x-date-active";
26403             setCellClass(this, cells[i]);
26404         }
26405         var extraDays = 0;
26406         for(; i < 42; i++) {
26407              textEls[i].innerHTML = (++extraDays);
26408              d.setDate(d.getDate()+1);
26409              cells[i].className = "x-date-nextday";
26410              setCellClass(this, cells[i]);
26411         }
26412
26413         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26414         this.fireEvent('monthchange', this, date);
26415         
26416         if(!this.internalRender){
26417             var main = this.el.dom.firstChild;
26418             var w = main.offsetWidth;
26419             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26420             Roo.fly(main).setWidth(w);
26421             this.internalRender = true;
26422             // opera does not respect the auto grow header center column
26423             // then, after it gets a width opera refuses to recalculate
26424             // without a second pass
26425             if(Roo.isOpera && !this.secondPass){
26426                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26427                 this.secondPass = true;
26428                 this.update.defer(10, this, [date]);
26429             }
26430         }
26431         
26432         
26433     }
26434 });        /*
26435  * Based on:
26436  * Ext JS Library 1.1.1
26437  * Copyright(c) 2006-2007, Ext JS, LLC.
26438  *
26439  * Originally Released Under LGPL - original licence link has changed is not relivant.
26440  *
26441  * Fork - LGPL
26442  * <script type="text/javascript">
26443  */
26444 /**
26445  * @class Roo.TabPanel
26446  * @extends Roo.util.Observable
26447  * A lightweight tab container.
26448  * <br><br>
26449  * Usage:
26450  * <pre><code>
26451 // basic tabs 1, built from existing content
26452 var tabs = new Roo.TabPanel("tabs1");
26453 tabs.addTab("script", "View Script");
26454 tabs.addTab("markup", "View Markup");
26455 tabs.activate("script");
26456
26457 // more advanced tabs, built from javascript
26458 var jtabs = new Roo.TabPanel("jtabs");
26459 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26460
26461 // set up the UpdateManager
26462 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26463 var updater = tab2.getUpdateManager();
26464 updater.setDefaultUrl("ajax1.htm");
26465 tab2.on('activate', updater.refresh, updater, true);
26466
26467 // Use setUrl for Ajax loading
26468 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26469 tab3.setUrl("ajax2.htm", null, true);
26470
26471 // Disabled tab
26472 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26473 tab4.disable();
26474
26475 jtabs.activate("jtabs-1");
26476  * </code></pre>
26477  * @constructor
26478  * Create a new TabPanel.
26479  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26480  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26481  */
26482 Roo.TabPanel = function(container, config){
26483     /**
26484     * The container element for this TabPanel.
26485     * @type Roo.Element
26486     */
26487     this.el = Roo.get(container, true);
26488     if(config){
26489         if(typeof config == "boolean"){
26490             this.tabPosition = config ? "bottom" : "top";
26491         }else{
26492             Roo.apply(this, config);
26493         }
26494     }
26495     if(this.tabPosition == "bottom"){
26496         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26497         this.el.addClass("x-tabs-bottom");
26498     }
26499     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26500     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26501     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26502     if(Roo.isIE){
26503         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26504     }
26505     if(this.tabPosition != "bottom"){
26506         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26507          * @type Roo.Element
26508          */
26509         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26510         this.el.addClass("x-tabs-top");
26511     }
26512     this.items = [];
26513
26514     this.bodyEl.setStyle("position", "relative");
26515
26516     this.active = null;
26517     this.activateDelegate = this.activate.createDelegate(this);
26518
26519     this.addEvents({
26520         /**
26521          * @event tabchange
26522          * Fires when the active tab changes
26523          * @param {Roo.TabPanel} this
26524          * @param {Roo.TabPanelItem} activePanel The new active tab
26525          */
26526         "tabchange": true,
26527         /**
26528          * @event beforetabchange
26529          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26530          * @param {Roo.TabPanel} this
26531          * @param {Object} e Set cancel to true on this object to cancel the tab change
26532          * @param {Roo.TabPanelItem} tab The tab being changed to
26533          */
26534         "beforetabchange" : true
26535     });
26536
26537     Roo.EventManager.onWindowResize(this.onResize, this);
26538     this.cpad = this.el.getPadding("lr");
26539     this.hiddenCount = 0;
26540
26541
26542     // toolbar on the tabbar support...
26543     if (this.toolbar) {
26544         var tcfg = this.toolbar;
26545         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26546         this.toolbar = new Roo.Toolbar(tcfg);
26547         if (Roo.isSafari) {
26548             var tbl = tcfg.container.child('table', true);
26549             tbl.setAttribute('width', '100%');
26550         }
26551         
26552     }
26553    
26554
26555
26556     Roo.TabPanel.superclass.constructor.call(this);
26557 };
26558
26559 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26560     /*
26561      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26562      */
26563     tabPosition : "top",
26564     /*
26565      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26566      */
26567     currentTabWidth : 0,
26568     /*
26569      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26570      */
26571     minTabWidth : 40,
26572     /*
26573      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26574      */
26575     maxTabWidth : 250,
26576     /*
26577      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26578      */
26579     preferredTabWidth : 175,
26580     /*
26581      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26582      */
26583     resizeTabs : false,
26584     /*
26585      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26586      */
26587     monitorResize : true,
26588     /*
26589      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26590      */
26591     toolbar : false,
26592
26593     /**
26594      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26595      * @param {String} id The id of the div to use <b>or create</b>
26596      * @param {String} text The text for the tab
26597      * @param {String} content (optional) Content to put in the TabPanelItem body
26598      * @param {Boolean} closable (optional) True to create a close icon on the tab
26599      * @return {Roo.TabPanelItem} The created TabPanelItem
26600      */
26601     addTab : function(id, text, content, closable){
26602         var item = new Roo.TabPanelItem(this, id, text, closable);
26603         this.addTabItem(item);
26604         if(content){
26605             item.setContent(content);
26606         }
26607         return item;
26608     },
26609
26610     /**
26611      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26612      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26613      * @return {Roo.TabPanelItem}
26614      */
26615     getTab : function(id){
26616         return this.items[id];
26617     },
26618
26619     /**
26620      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26621      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26622      */
26623     hideTab : function(id){
26624         var t = this.items[id];
26625         if(!t.isHidden()){
26626            t.setHidden(true);
26627            this.hiddenCount++;
26628            this.autoSizeTabs();
26629         }
26630     },
26631
26632     /**
26633      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26634      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26635      */
26636     unhideTab : function(id){
26637         var t = this.items[id];
26638         if(t.isHidden()){
26639            t.setHidden(false);
26640            this.hiddenCount--;
26641            this.autoSizeTabs();
26642         }
26643     },
26644
26645     /**
26646      * Adds an existing {@link Roo.TabPanelItem}.
26647      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26648      */
26649     addTabItem : function(item){
26650         this.items[item.id] = item;
26651         this.items.push(item);
26652         if(this.resizeTabs){
26653            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26654            this.autoSizeTabs();
26655         }else{
26656             item.autoSize();
26657         }
26658     },
26659
26660     /**
26661      * Removes a {@link Roo.TabPanelItem}.
26662      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26663      */
26664     removeTab : function(id){
26665         var items = this.items;
26666         var tab = items[id];
26667         if(!tab) { return; }
26668         var index = items.indexOf(tab);
26669         if(this.active == tab && items.length > 1){
26670             var newTab = this.getNextAvailable(index);
26671             if(newTab) {
26672                 newTab.activate();
26673             }
26674         }
26675         this.stripEl.dom.removeChild(tab.pnode.dom);
26676         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26677             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26678         }
26679         items.splice(index, 1);
26680         delete this.items[tab.id];
26681         tab.fireEvent("close", tab);
26682         tab.purgeListeners();
26683         this.autoSizeTabs();
26684     },
26685
26686     getNextAvailable : function(start){
26687         var items = this.items;
26688         var index = start;
26689         // look for a next tab that will slide over to
26690         // replace the one being removed
26691         while(index < items.length){
26692             var item = items[++index];
26693             if(item && !item.isHidden()){
26694                 return item;
26695             }
26696         }
26697         // if one isn't found select the previous tab (on the left)
26698         index = start;
26699         while(index >= 0){
26700             var item = items[--index];
26701             if(item && !item.isHidden()){
26702                 return item;
26703             }
26704         }
26705         return null;
26706     },
26707
26708     /**
26709      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26710      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26711      */
26712     disableTab : function(id){
26713         var tab = this.items[id];
26714         if(tab && this.active != tab){
26715             tab.disable();
26716         }
26717     },
26718
26719     /**
26720      * Enables a {@link Roo.TabPanelItem} that is disabled.
26721      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26722      */
26723     enableTab : function(id){
26724         var tab = this.items[id];
26725         tab.enable();
26726     },
26727
26728     /**
26729      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26730      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26731      * @return {Roo.TabPanelItem} The TabPanelItem.
26732      */
26733     activate : function(id){
26734         var tab = this.items[id];
26735         if(!tab){
26736             return null;
26737         }
26738         if(tab == this.active || tab.disabled){
26739             return tab;
26740         }
26741         var e = {};
26742         this.fireEvent("beforetabchange", this, e, tab);
26743         if(e.cancel !== true && !tab.disabled){
26744             if(this.active){
26745                 this.active.hide();
26746             }
26747             this.active = this.items[id];
26748             this.active.show();
26749             this.fireEvent("tabchange", this, this.active);
26750         }
26751         return tab;
26752     },
26753
26754     /**
26755      * Gets the active {@link Roo.TabPanelItem}.
26756      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26757      */
26758     getActiveTab : function(){
26759         return this.active;
26760     },
26761
26762     /**
26763      * Updates the tab body element to fit the height of the container element
26764      * for overflow scrolling
26765      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26766      */
26767     syncHeight : function(targetHeight){
26768         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26769         var bm = this.bodyEl.getMargins();
26770         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26771         this.bodyEl.setHeight(newHeight);
26772         return newHeight;
26773     },
26774
26775     onResize : function(){
26776         if(this.monitorResize){
26777             this.autoSizeTabs();
26778         }
26779     },
26780
26781     /**
26782      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26783      */
26784     beginUpdate : function(){
26785         this.updating = true;
26786     },
26787
26788     /**
26789      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26790      */
26791     endUpdate : function(){
26792         this.updating = false;
26793         this.autoSizeTabs();
26794     },
26795
26796     /**
26797      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26798      */
26799     autoSizeTabs : function(){
26800         var count = this.items.length;
26801         var vcount = count - this.hiddenCount;
26802         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26803         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26804         var availWidth = Math.floor(w / vcount);
26805         var b = this.stripBody;
26806         if(b.getWidth() > w){
26807             var tabs = this.items;
26808             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26809             if(availWidth < this.minTabWidth){
26810                 /*if(!this.sleft){    // incomplete scrolling code
26811                     this.createScrollButtons();
26812                 }
26813                 this.showScroll();
26814                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26815             }
26816         }else{
26817             if(this.currentTabWidth < this.preferredTabWidth){
26818                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26819             }
26820         }
26821     },
26822
26823     /**
26824      * Returns the number of tabs in this TabPanel.
26825      * @return {Number}
26826      */
26827      getCount : function(){
26828          return this.items.length;
26829      },
26830
26831     /**
26832      * Resizes all the tabs to the passed width
26833      * @param {Number} The new width
26834      */
26835     setTabWidth : function(width){
26836         this.currentTabWidth = width;
26837         for(var i = 0, len = this.items.length; i < len; i++) {
26838                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26839         }
26840     },
26841
26842     /**
26843      * Destroys this TabPanel
26844      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26845      */
26846     destroy : function(removeEl){
26847         Roo.EventManager.removeResizeListener(this.onResize, this);
26848         for(var i = 0, len = this.items.length; i < len; i++){
26849             this.items[i].purgeListeners();
26850         }
26851         if(removeEl === true){
26852             this.el.update("");
26853             this.el.remove();
26854         }
26855     }
26856 });
26857
26858 /**
26859  * @class Roo.TabPanelItem
26860  * @extends Roo.util.Observable
26861  * Represents an individual item (tab plus body) in a TabPanel.
26862  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26863  * @param {String} id The id of this TabPanelItem
26864  * @param {String} text The text for the tab of this TabPanelItem
26865  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26866  */
26867 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26868     /**
26869      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26870      * @type Roo.TabPanel
26871      */
26872     this.tabPanel = tabPanel;
26873     /**
26874      * The id for this TabPanelItem
26875      * @type String
26876      */
26877     this.id = id;
26878     /** @private */
26879     this.disabled = false;
26880     /** @private */
26881     this.text = text;
26882     /** @private */
26883     this.loaded = false;
26884     this.closable = closable;
26885
26886     /**
26887      * The body element for this TabPanelItem.
26888      * @type Roo.Element
26889      */
26890     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26891     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26892     this.bodyEl.setStyle("display", "block");
26893     this.bodyEl.setStyle("zoom", "1");
26894     this.hideAction();
26895
26896     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26897     /** @private */
26898     this.el = Roo.get(els.el, true);
26899     this.inner = Roo.get(els.inner, true);
26900     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26901     this.pnode = Roo.get(els.el.parentNode, true);
26902     this.el.on("mousedown", this.onTabMouseDown, this);
26903     this.el.on("click", this.onTabClick, this);
26904     /** @private */
26905     if(closable){
26906         var c = Roo.get(els.close, true);
26907         c.dom.title = this.closeText;
26908         c.addClassOnOver("close-over");
26909         c.on("click", this.closeClick, this);
26910      }
26911
26912     this.addEvents({
26913          /**
26914          * @event activate
26915          * Fires when this tab becomes the active tab.
26916          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26917          * @param {Roo.TabPanelItem} this
26918          */
26919         "activate": true,
26920         /**
26921          * @event beforeclose
26922          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26923          * @param {Roo.TabPanelItem} this
26924          * @param {Object} e Set cancel to true on this object to cancel the close.
26925          */
26926         "beforeclose": true,
26927         /**
26928          * @event close
26929          * Fires when this tab is closed.
26930          * @param {Roo.TabPanelItem} this
26931          */
26932          "close": true,
26933         /**
26934          * @event deactivate
26935          * Fires when this tab is no longer the active tab.
26936          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26937          * @param {Roo.TabPanelItem} this
26938          */
26939          "deactivate" : true
26940     });
26941     this.hidden = false;
26942
26943     Roo.TabPanelItem.superclass.constructor.call(this);
26944 };
26945
26946 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26947     purgeListeners : function(){
26948        Roo.util.Observable.prototype.purgeListeners.call(this);
26949        this.el.removeAllListeners();
26950     },
26951     /**
26952      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26953      */
26954     show : function(){
26955         this.pnode.addClass("on");
26956         this.showAction();
26957         if(Roo.isOpera){
26958             this.tabPanel.stripWrap.repaint();
26959         }
26960         this.fireEvent("activate", this.tabPanel, this);
26961     },
26962
26963     /**
26964      * Returns true if this tab is the active tab.
26965      * @return {Boolean}
26966      */
26967     isActive : function(){
26968         return this.tabPanel.getActiveTab() == this;
26969     },
26970
26971     /**
26972      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26973      */
26974     hide : function(){
26975         this.pnode.removeClass("on");
26976         this.hideAction();
26977         this.fireEvent("deactivate", this.tabPanel, this);
26978     },
26979
26980     hideAction : function(){
26981         this.bodyEl.hide();
26982         this.bodyEl.setStyle("position", "absolute");
26983         this.bodyEl.setLeft("-20000px");
26984         this.bodyEl.setTop("-20000px");
26985     },
26986
26987     showAction : function(){
26988         this.bodyEl.setStyle("position", "relative");
26989         this.bodyEl.setTop("");
26990         this.bodyEl.setLeft("");
26991         this.bodyEl.show();
26992     },
26993
26994     /**
26995      * Set the tooltip for the tab.
26996      * @param {String} tooltip The tab's tooltip
26997      */
26998     setTooltip : function(text){
26999         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27000             this.textEl.dom.qtip = text;
27001             this.textEl.dom.removeAttribute('title');
27002         }else{
27003             this.textEl.dom.title = text;
27004         }
27005     },
27006
27007     onTabClick : function(e){
27008         e.preventDefault();
27009         this.tabPanel.activate(this.id);
27010     },
27011
27012     onTabMouseDown : function(e){
27013         e.preventDefault();
27014         this.tabPanel.activate(this.id);
27015     },
27016
27017     getWidth : function(){
27018         return this.inner.getWidth();
27019     },
27020
27021     setWidth : function(width){
27022         var iwidth = width - this.pnode.getPadding("lr");
27023         this.inner.setWidth(iwidth);
27024         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27025         this.pnode.setWidth(width);
27026     },
27027
27028     /**
27029      * Show or hide the tab
27030      * @param {Boolean} hidden True to hide or false to show.
27031      */
27032     setHidden : function(hidden){
27033         this.hidden = hidden;
27034         this.pnode.setStyle("display", hidden ? "none" : "");
27035     },
27036
27037     /**
27038      * Returns true if this tab is "hidden"
27039      * @return {Boolean}
27040      */
27041     isHidden : function(){
27042         return this.hidden;
27043     },
27044
27045     /**
27046      * Returns the text for this tab
27047      * @return {String}
27048      */
27049     getText : function(){
27050         return this.text;
27051     },
27052
27053     autoSize : function(){
27054         //this.el.beginMeasure();
27055         this.textEl.setWidth(1);
27056         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27057         //this.el.endMeasure();
27058     },
27059
27060     /**
27061      * Sets the text for the tab (Note: this also sets the tooltip text)
27062      * @param {String} text The tab's text and tooltip
27063      */
27064     setText : function(text){
27065         this.text = text;
27066         this.textEl.update(text);
27067         this.setTooltip(text);
27068         if(!this.tabPanel.resizeTabs){
27069             this.autoSize();
27070         }
27071     },
27072     /**
27073      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27074      */
27075     activate : function(){
27076         this.tabPanel.activate(this.id);
27077     },
27078
27079     /**
27080      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27081      */
27082     disable : function(){
27083         if(this.tabPanel.active != this){
27084             this.disabled = true;
27085             this.pnode.addClass("disabled");
27086         }
27087     },
27088
27089     /**
27090      * Enables this TabPanelItem if it was previously disabled.
27091      */
27092     enable : function(){
27093         this.disabled = false;
27094         this.pnode.removeClass("disabled");
27095     },
27096
27097     /**
27098      * Sets the content for this TabPanelItem.
27099      * @param {String} content The content
27100      * @param {Boolean} loadScripts true to look for and load scripts
27101      */
27102     setContent : function(content, loadScripts){
27103         this.bodyEl.update(content, loadScripts);
27104     },
27105
27106     /**
27107      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27108      * @return {Roo.UpdateManager} The UpdateManager
27109      */
27110     getUpdateManager : function(){
27111         return this.bodyEl.getUpdateManager();
27112     },
27113
27114     /**
27115      * Set a URL to be used to load the content for this TabPanelItem.
27116      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27117      * @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)
27118      * @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)
27119      * @return {Roo.UpdateManager} The UpdateManager
27120      */
27121     setUrl : function(url, params, loadOnce){
27122         if(this.refreshDelegate){
27123             this.un('activate', this.refreshDelegate);
27124         }
27125         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27126         this.on("activate", this.refreshDelegate);
27127         return this.bodyEl.getUpdateManager();
27128     },
27129
27130     /** @private */
27131     _handleRefresh : function(url, params, loadOnce){
27132         if(!loadOnce || !this.loaded){
27133             var updater = this.bodyEl.getUpdateManager();
27134             updater.update(url, params, this._setLoaded.createDelegate(this));
27135         }
27136     },
27137
27138     /**
27139      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27140      *   Will fail silently if the setUrl method has not been called.
27141      *   This does not activate the panel, just updates its content.
27142      */
27143     refresh : function(){
27144         if(this.refreshDelegate){
27145            this.loaded = false;
27146            this.refreshDelegate();
27147         }
27148     },
27149
27150     /** @private */
27151     _setLoaded : function(){
27152         this.loaded = true;
27153     },
27154
27155     /** @private */
27156     closeClick : function(e){
27157         var o = {};
27158         e.stopEvent();
27159         this.fireEvent("beforeclose", this, o);
27160         if(o.cancel !== true){
27161             this.tabPanel.removeTab(this.id);
27162         }
27163     },
27164     /**
27165      * The text displayed in the tooltip for the close icon.
27166      * @type String
27167      */
27168     closeText : "Close this tab"
27169 });
27170
27171 /** @private */
27172 Roo.TabPanel.prototype.createStrip = function(container){
27173     var strip = document.createElement("div");
27174     strip.className = "x-tabs-wrap";
27175     container.appendChild(strip);
27176     return strip;
27177 };
27178 /** @private */
27179 Roo.TabPanel.prototype.createStripList = function(strip){
27180     // div wrapper for retard IE
27181     // returns the "tr" element.
27182     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27183         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27184         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27185     return strip.firstChild.firstChild.firstChild.firstChild;
27186 };
27187 /** @private */
27188 Roo.TabPanel.prototype.createBody = function(container){
27189     var body = document.createElement("div");
27190     Roo.id(body, "tab-body");
27191     Roo.fly(body).addClass("x-tabs-body");
27192     container.appendChild(body);
27193     return body;
27194 };
27195 /** @private */
27196 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27197     var body = Roo.getDom(id);
27198     if(!body){
27199         body = document.createElement("div");
27200         body.id = id;
27201     }
27202     Roo.fly(body).addClass("x-tabs-item-body");
27203     bodyEl.insertBefore(body, bodyEl.firstChild);
27204     return body;
27205 };
27206 /** @private */
27207 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27208     var td = document.createElement("td");
27209     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27210     //stripEl.appendChild(td);
27211     if(closable){
27212         td.className = "x-tabs-closable";
27213         if(!this.closeTpl){
27214             this.closeTpl = new Roo.Template(
27215                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27216                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27217                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27218             );
27219         }
27220         var el = this.closeTpl.overwrite(td, {"text": text});
27221         var close = el.getElementsByTagName("div")[0];
27222         var inner = el.getElementsByTagName("em")[0];
27223         return {"el": el, "close": close, "inner": inner};
27224     } else {
27225         if(!this.tabTpl){
27226             this.tabTpl = new Roo.Template(
27227                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27228                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27229             );
27230         }
27231         var el = this.tabTpl.overwrite(td, {"text": text});
27232         var inner = el.getElementsByTagName("em")[0];
27233         return {"el": el, "inner": inner};
27234     }
27235 };/*
27236  * Based on:
27237  * Ext JS Library 1.1.1
27238  * Copyright(c) 2006-2007, Ext JS, LLC.
27239  *
27240  * Originally Released Under LGPL - original licence link has changed is not relivant.
27241  *
27242  * Fork - LGPL
27243  * <script type="text/javascript">
27244  */
27245
27246 /**
27247  * @class Roo.Button
27248  * @extends Roo.util.Observable
27249  * Simple Button class
27250  * @cfg {String} text The button text
27251  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27252  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27253  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27254  * @cfg {Object} scope The scope of the handler
27255  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27256  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27257  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27258  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27259  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27260  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27261    applies if enableToggle = true)
27262  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27263  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27264   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27265  * @constructor
27266  * Create a new button
27267  * @param {Object} config The config object
27268  */
27269 Roo.Button = function(renderTo, config)
27270 {
27271     if (!config) {
27272         config = renderTo;
27273         renderTo = config.renderTo || false;
27274     }
27275     
27276     Roo.apply(this, config);
27277     this.addEvents({
27278         /**
27279              * @event click
27280              * Fires when this button is clicked
27281              * @param {Button} this
27282              * @param {EventObject} e The click event
27283              */
27284             "click" : true,
27285         /**
27286              * @event toggle
27287              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27288              * @param {Button} this
27289              * @param {Boolean} pressed
27290              */
27291             "toggle" : true,
27292         /**
27293              * @event mouseover
27294              * Fires when the mouse hovers over the button
27295              * @param {Button} this
27296              * @param {Event} e The event object
27297              */
27298         'mouseover' : true,
27299         /**
27300              * @event mouseout
27301              * Fires when the mouse exits the button
27302              * @param {Button} this
27303              * @param {Event} e The event object
27304              */
27305         'mouseout': true,
27306          /**
27307              * @event render
27308              * Fires when the button is rendered
27309              * @param {Button} this
27310              */
27311         'render': true
27312     });
27313     if(this.menu){
27314         this.menu = Roo.menu.MenuMgr.get(this.menu);
27315     }
27316     // register listeners first!!  - so render can be captured..
27317     Roo.util.Observable.call(this);
27318     if(renderTo){
27319         this.render(renderTo);
27320     }
27321     
27322   
27323 };
27324
27325 Roo.extend(Roo.Button, Roo.util.Observable, {
27326     /**
27327      * 
27328      */
27329     
27330     /**
27331      * Read-only. True if this button is hidden
27332      * @type Boolean
27333      */
27334     hidden : false,
27335     /**
27336      * Read-only. True if this button is disabled
27337      * @type Boolean
27338      */
27339     disabled : false,
27340     /**
27341      * Read-only. True if this button is pressed (only if enableToggle = true)
27342      * @type Boolean
27343      */
27344     pressed : false,
27345
27346     /**
27347      * @cfg {Number} tabIndex 
27348      * The DOM tabIndex for this button (defaults to undefined)
27349      */
27350     tabIndex : undefined,
27351
27352     /**
27353      * @cfg {Boolean} enableToggle
27354      * True to enable pressed/not pressed toggling (defaults to false)
27355      */
27356     enableToggle: false,
27357     /**
27358      * @cfg {Mixed} menu
27359      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27360      */
27361     menu : undefined,
27362     /**
27363      * @cfg {String} menuAlign
27364      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27365      */
27366     menuAlign : "tl-bl?",
27367
27368     /**
27369      * @cfg {String} iconCls
27370      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27371      */
27372     iconCls : undefined,
27373     /**
27374      * @cfg {String} type
27375      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27376      */
27377     type : 'button',
27378
27379     // private
27380     menuClassTarget: 'tr',
27381
27382     /**
27383      * @cfg {String} clickEvent
27384      * The type of event to map to the button's event handler (defaults to 'click')
27385      */
27386     clickEvent : 'click',
27387
27388     /**
27389      * @cfg {Boolean} handleMouseEvents
27390      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27391      */
27392     handleMouseEvents : true,
27393
27394     /**
27395      * @cfg {String} tooltipType
27396      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27397      */
27398     tooltipType : 'qtip',
27399
27400     /**
27401      * @cfg {String} cls
27402      * A CSS class to apply to the button's main element.
27403      */
27404     
27405     /**
27406      * @cfg {Roo.Template} template (Optional)
27407      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27408      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27409      * require code modifications if required elements (e.g. a button) aren't present.
27410      */
27411
27412     // private
27413     render : function(renderTo){
27414         var btn;
27415         if(this.hideParent){
27416             this.parentEl = Roo.get(renderTo);
27417         }
27418         if(!this.dhconfig){
27419             if(!this.template){
27420                 if(!Roo.Button.buttonTemplate){
27421                     // hideous table template
27422                     Roo.Button.buttonTemplate = new Roo.Template(
27423                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27424                         '<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>',
27425                         "</tr></tbody></table>");
27426                 }
27427                 this.template = Roo.Button.buttonTemplate;
27428             }
27429             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27430             var btnEl = btn.child("button:first");
27431             btnEl.on('focus', this.onFocus, this);
27432             btnEl.on('blur', this.onBlur, this);
27433             if(this.cls){
27434                 btn.addClass(this.cls);
27435             }
27436             if(this.icon){
27437                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27438             }
27439             if(this.iconCls){
27440                 btnEl.addClass(this.iconCls);
27441                 if(!this.cls){
27442                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27443                 }
27444             }
27445             if(this.tabIndex !== undefined){
27446                 btnEl.dom.tabIndex = this.tabIndex;
27447             }
27448             if(this.tooltip){
27449                 if(typeof this.tooltip == 'object'){
27450                     Roo.QuickTips.tips(Roo.apply({
27451                           target: btnEl.id
27452                     }, this.tooltip));
27453                 } else {
27454                     btnEl.dom[this.tooltipType] = this.tooltip;
27455                 }
27456             }
27457         }else{
27458             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27459         }
27460         this.el = btn;
27461         if(this.id){
27462             this.el.dom.id = this.el.id = this.id;
27463         }
27464         if(this.menu){
27465             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27466             this.menu.on("show", this.onMenuShow, this);
27467             this.menu.on("hide", this.onMenuHide, this);
27468         }
27469         btn.addClass("x-btn");
27470         if(Roo.isIE && !Roo.isIE7){
27471             this.autoWidth.defer(1, this);
27472         }else{
27473             this.autoWidth();
27474         }
27475         if(this.handleMouseEvents){
27476             btn.on("mouseover", this.onMouseOver, this);
27477             btn.on("mouseout", this.onMouseOut, this);
27478             btn.on("mousedown", this.onMouseDown, this);
27479         }
27480         btn.on(this.clickEvent, this.onClick, this);
27481         //btn.on("mouseup", this.onMouseUp, this);
27482         if(this.hidden){
27483             this.hide();
27484         }
27485         if(this.disabled){
27486             this.disable();
27487         }
27488         Roo.ButtonToggleMgr.register(this);
27489         if(this.pressed){
27490             this.el.addClass("x-btn-pressed");
27491         }
27492         if(this.repeat){
27493             var repeater = new Roo.util.ClickRepeater(btn,
27494                 typeof this.repeat == "object" ? this.repeat : {}
27495             );
27496             repeater.on("click", this.onClick,  this);
27497         }
27498         
27499         this.fireEvent('render', this);
27500         
27501     },
27502     /**
27503      * Returns the button's underlying element
27504      * @return {Roo.Element} The element
27505      */
27506     getEl : function(){
27507         return this.el;  
27508     },
27509     
27510     /**
27511      * Destroys this Button and removes any listeners.
27512      */
27513     destroy : function(){
27514         Roo.ButtonToggleMgr.unregister(this);
27515         this.el.removeAllListeners();
27516         this.purgeListeners();
27517         this.el.remove();
27518     },
27519
27520     // private
27521     autoWidth : function(){
27522         if(this.el){
27523             this.el.setWidth("auto");
27524             if(Roo.isIE7 && Roo.isStrict){
27525                 var ib = this.el.child('button');
27526                 if(ib && ib.getWidth() > 20){
27527                     ib.clip();
27528                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27529                 }
27530             }
27531             if(this.minWidth){
27532                 if(this.hidden){
27533                     this.el.beginMeasure();
27534                 }
27535                 if(this.el.getWidth() < this.minWidth){
27536                     this.el.setWidth(this.minWidth);
27537                 }
27538                 if(this.hidden){
27539                     this.el.endMeasure();
27540                 }
27541             }
27542         }
27543     },
27544
27545     /**
27546      * Assigns this button's click handler
27547      * @param {Function} handler The function to call when the button is clicked
27548      * @param {Object} scope (optional) Scope for the function passed in
27549      */
27550     setHandler : function(handler, scope){
27551         this.handler = handler;
27552         this.scope = scope;  
27553     },
27554     
27555     /**
27556      * Sets this button's text
27557      * @param {String} text The button text
27558      */
27559     setText : function(text){
27560         this.text = text;
27561         if(this.el){
27562             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27563         }
27564         this.autoWidth();
27565     },
27566     
27567     /**
27568      * Gets the text for this button
27569      * @return {String} The button text
27570      */
27571     getText : function(){
27572         return this.text;  
27573     },
27574     
27575     /**
27576      * Show this button
27577      */
27578     show: function(){
27579         this.hidden = false;
27580         if(this.el){
27581             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27582         }
27583     },
27584     
27585     /**
27586      * Hide this button
27587      */
27588     hide: function(){
27589         this.hidden = true;
27590         if(this.el){
27591             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27592         }
27593     },
27594     
27595     /**
27596      * Convenience function for boolean show/hide
27597      * @param {Boolean} visible True to show, false to hide
27598      */
27599     setVisible: function(visible){
27600         if(visible) {
27601             this.show();
27602         }else{
27603             this.hide();
27604         }
27605     },
27606     
27607     /**
27608      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27609      * @param {Boolean} state (optional) Force a particular state
27610      */
27611     toggle : function(state){
27612         state = state === undefined ? !this.pressed : state;
27613         if(state != this.pressed){
27614             if(state){
27615                 this.el.addClass("x-btn-pressed");
27616                 this.pressed = true;
27617                 this.fireEvent("toggle", this, true);
27618             }else{
27619                 this.el.removeClass("x-btn-pressed");
27620                 this.pressed = false;
27621                 this.fireEvent("toggle", this, false);
27622             }
27623             if(this.toggleHandler){
27624                 this.toggleHandler.call(this.scope || this, this, state);
27625             }
27626         }
27627     },
27628     
27629     /**
27630      * Focus the button
27631      */
27632     focus : function(){
27633         this.el.child('button:first').focus();
27634     },
27635     
27636     /**
27637      * Disable this button
27638      */
27639     disable : function(){
27640         if(this.el){
27641             this.el.addClass("x-btn-disabled");
27642         }
27643         this.disabled = true;
27644     },
27645     
27646     /**
27647      * Enable this button
27648      */
27649     enable : function(){
27650         if(this.el){
27651             this.el.removeClass("x-btn-disabled");
27652         }
27653         this.disabled = false;
27654     },
27655
27656     /**
27657      * Convenience function for boolean enable/disable
27658      * @param {Boolean} enabled True to enable, false to disable
27659      */
27660     setDisabled : function(v){
27661         this[v !== true ? "enable" : "disable"]();
27662     },
27663
27664     // private
27665     onClick : function(e){
27666         if(e){
27667             e.preventDefault();
27668         }
27669         if(e.button != 0){
27670             return;
27671         }
27672         if(!this.disabled){
27673             if(this.enableToggle){
27674                 this.toggle();
27675             }
27676             if(this.menu && !this.menu.isVisible()){
27677                 this.menu.show(this.el, this.menuAlign);
27678             }
27679             this.fireEvent("click", this, e);
27680             if(this.handler){
27681                 this.el.removeClass("x-btn-over");
27682                 this.handler.call(this.scope || this, this, e);
27683             }
27684         }
27685     },
27686     // private
27687     onMouseOver : function(e){
27688         if(!this.disabled){
27689             this.el.addClass("x-btn-over");
27690             this.fireEvent('mouseover', this, e);
27691         }
27692     },
27693     // private
27694     onMouseOut : function(e){
27695         if(!e.within(this.el,  true)){
27696             this.el.removeClass("x-btn-over");
27697             this.fireEvent('mouseout', this, e);
27698         }
27699     },
27700     // private
27701     onFocus : function(e){
27702         if(!this.disabled){
27703             this.el.addClass("x-btn-focus");
27704         }
27705     },
27706     // private
27707     onBlur : function(e){
27708         this.el.removeClass("x-btn-focus");
27709     },
27710     // private
27711     onMouseDown : function(e){
27712         if(!this.disabled && e.button == 0){
27713             this.el.addClass("x-btn-click");
27714             Roo.get(document).on('mouseup', this.onMouseUp, this);
27715         }
27716     },
27717     // private
27718     onMouseUp : function(e){
27719         if(e.button == 0){
27720             this.el.removeClass("x-btn-click");
27721             Roo.get(document).un('mouseup', this.onMouseUp, this);
27722         }
27723     },
27724     // private
27725     onMenuShow : function(e){
27726         this.el.addClass("x-btn-menu-active");
27727     },
27728     // private
27729     onMenuHide : function(e){
27730         this.el.removeClass("x-btn-menu-active");
27731     }   
27732 });
27733
27734 // Private utility class used by Button
27735 Roo.ButtonToggleMgr = function(){
27736    var groups = {};
27737    
27738    function toggleGroup(btn, state){
27739        if(state){
27740            var g = groups[btn.toggleGroup];
27741            for(var i = 0, l = g.length; i < l; i++){
27742                if(g[i] != btn){
27743                    g[i].toggle(false);
27744                }
27745            }
27746        }
27747    }
27748    
27749    return {
27750        register : function(btn){
27751            if(!btn.toggleGroup){
27752                return;
27753            }
27754            var g = groups[btn.toggleGroup];
27755            if(!g){
27756                g = groups[btn.toggleGroup] = [];
27757            }
27758            g.push(btn);
27759            btn.on("toggle", toggleGroup);
27760        },
27761        
27762        unregister : function(btn){
27763            if(!btn.toggleGroup){
27764                return;
27765            }
27766            var g = groups[btn.toggleGroup];
27767            if(g){
27768                g.remove(btn);
27769                btn.un("toggle", toggleGroup);
27770            }
27771        }
27772    };
27773 }();/*
27774  * Based on:
27775  * Ext JS Library 1.1.1
27776  * Copyright(c) 2006-2007, Ext JS, LLC.
27777  *
27778  * Originally Released Under LGPL - original licence link has changed is not relivant.
27779  *
27780  * Fork - LGPL
27781  * <script type="text/javascript">
27782  */
27783  
27784 /**
27785  * @class Roo.SplitButton
27786  * @extends Roo.Button
27787  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27788  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27789  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27790  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27791  * @cfg {String} arrowTooltip The title attribute of the arrow
27792  * @constructor
27793  * Create a new menu button
27794  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27795  * @param {Object} config The config object
27796  */
27797 Roo.SplitButton = function(renderTo, config){
27798     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27799     /**
27800      * @event arrowclick
27801      * Fires when this button's arrow is clicked
27802      * @param {SplitButton} this
27803      * @param {EventObject} e The click event
27804      */
27805     this.addEvents({"arrowclick":true});
27806 };
27807
27808 Roo.extend(Roo.SplitButton, Roo.Button, {
27809     render : function(renderTo){
27810         // this is one sweet looking template!
27811         var tpl = new Roo.Template(
27812             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27813             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27814             '<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>',
27815             "</tbody></table></td><td>",
27816             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27817             '<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>',
27818             "</tbody></table></td></tr></table>"
27819         );
27820         var btn = tpl.append(renderTo, [this.text, this.type], true);
27821         var btnEl = btn.child("button");
27822         if(this.cls){
27823             btn.addClass(this.cls);
27824         }
27825         if(this.icon){
27826             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27827         }
27828         if(this.iconCls){
27829             btnEl.addClass(this.iconCls);
27830             if(!this.cls){
27831                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27832             }
27833         }
27834         this.el = btn;
27835         if(this.handleMouseEvents){
27836             btn.on("mouseover", this.onMouseOver, this);
27837             btn.on("mouseout", this.onMouseOut, this);
27838             btn.on("mousedown", this.onMouseDown, this);
27839             btn.on("mouseup", this.onMouseUp, this);
27840         }
27841         btn.on(this.clickEvent, this.onClick, this);
27842         if(this.tooltip){
27843             if(typeof this.tooltip == 'object'){
27844                 Roo.QuickTips.tips(Roo.apply({
27845                       target: btnEl.id
27846                 }, this.tooltip));
27847             } else {
27848                 btnEl.dom[this.tooltipType] = this.tooltip;
27849             }
27850         }
27851         if(this.arrowTooltip){
27852             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27853         }
27854         if(this.hidden){
27855             this.hide();
27856         }
27857         if(this.disabled){
27858             this.disable();
27859         }
27860         if(this.pressed){
27861             this.el.addClass("x-btn-pressed");
27862         }
27863         if(Roo.isIE && !Roo.isIE7){
27864             this.autoWidth.defer(1, this);
27865         }else{
27866             this.autoWidth();
27867         }
27868         if(this.menu){
27869             this.menu.on("show", this.onMenuShow, this);
27870             this.menu.on("hide", this.onMenuHide, this);
27871         }
27872         this.fireEvent('render', this);
27873     },
27874
27875     // private
27876     autoWidth : function(){
27877         if(this.el){
27878             var tbl = this.el.child("table:first");
27879             var tbl2 = this.el.child("table:last");
27880             this.el.setWidth("auto");
27881             tbl.setWidth("auto");
27882             if(Roo.isIE7 && Roo.isStrict){
27883                 var ib = this.el.child('button:first');
27884                 if(ib && ib.getWidth() > 20){
27885                     ib.clip();
27886                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27887                 }
27888             }
27889             if(this.minWidth){
27890                 if(this.hidden){
27891                     this.el.beginMeasure();
27892                 }
27893                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27894                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27895                 }
27896                 if(this.hidden){
27897                     this.el.endMeasure();
27898                 }
27899             }
27900             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27901         } 
27902     },
27903     /**
27904      * Sets this button's click handler
27905      * @param {Function} handler The function to call when the button is clicked
27906      * @param {Object} scope (optional) Scope for the function passed above
27907      */
27908     setHandler : function(handler, scope){
27909         this.handler = handler;
27910         this.scope = scope;  
27911     },
27912     
27913     /**
27914      * Sets this button's arrow click handler
27915      * @param {Function} handler The function to call when the arrow is clicked
27916      * @param {Object} scope (optional) Scope for the function passed above
27917      */
27918     setArrowHandler : function(handler, scope){
27919         this.arrowHandler = handler;
27920         this.scope = scope;  
27921     },
27922     
27923     /**
27924      * Focus the button
27925      */
27926     focus : function(){
27927         if(this.el){
27928             this.el.child("button:first").focus();
27929         }
27930     },
27931
27932     // private
27933     onClick : function(e){
27934         e.preventDefault();
27935         if(!this.disabled){
27936             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27937                 if(this.menu && !this.menu.isVisible()){
27938                     this.menu.show(this.el, this.menuAlign);
27939                 }
27940                 this.fireEvent("arrowclick", this, e);
27941                 if(this.arrowHandler){
27942                     this.arrowHandler.call(this.scope || this, this, e);
27943                 }
27944             }else{
27945                 this.fireEvent("click", this, e);
27946                 if(this.handler){
27947                     this.handler.call(this.scope || this, this, e);
27948                 }
27949             }
27950         }
27951     },
27952     // private
27953     onMouseDown : function(e){
27954         if(!this.disabled){
27955             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27956         }
27957     },
27958     // private
27959     onMouseUp : function(e){
27960         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27961     }   
27962 });
27963
27964
27965 // backwards compat
27966 Roo.MenuButton = Roo.SplitButton;/*
27967  * Based on:
27968  * Ext JS Library 1.1.1
27969  * Copyright(c) 2006-2007, Ext JS, LLC.
27970  *
27971  * Originally Released Under LGPL - original licence link has changed is not relivant.
27972  *
27973  * Fork - LGPL
27974  * <script type="text/javascript">
27975  */
27976
27977 /**
27978  * @class Roo.Toolbar
27979  * Basic Toolbar class.
27980  * @constructor
27981  * Creates a new Toolbar
27982  * @param {Object} container The config object
27983  */ 
27984 Roo.Toolbar = function(container, buttons, config)
27985 {
27986     /// old consturctor format still supported..
27987     if(container instanceof Array){ // omit the container for later rendering
27988         buttons = container;
27989         config = buttons;
27990         container = null;
27991     }
27992     if (typeof(container) == 'object' && container.xtype) {
27993         config = container;
27994         container = config.container;
27995         buttons = config.buttons || []; // not really - use items!!
27996     }
27997     var xitems = [];
27998     if (config && config.items) {
27999         xitems = config.items;
28000         delete config.items;
28001     }
28002     Roo.apply(this, config);
28003     this.buttons = buttons;
28004     
28005     if(container){
28006         this.render(container);
28007     }
28008     this.xitems = xitems;
28009     Roo.each(xitems, function(b) {
28010         this.add(b);
28011     }, this);
28012     
28013 };
28014
28015 Roo.Toolbar.prototype = {
28016     /**
28017      * @cfg {Array} items
28018      * array of button configs or elements to add (will be converted to a MixedCollection)
28019      */
28020     
28021     /**
28022      * @cfg {String/HTMLElement/Element} container
28023      * The id or element that will contain the toolbar
28024      */
28025     // private
28026     render : function(ct){
28027         this.el = Roo.get(ct);
28028         if(this.cls){
28029             this.el.addClass(this.cls);
28030         }
28031         // using a table allows for vertical alignment
28032         // 100% width is needed by Safari...
28033         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28034         this.tr = this.el.child("tr", true);
28035         var autoId = 0;
28036         this.items = new Roo.util.MixedCollection(false, function(o){
28037             return o.id || ("item" + (++autoId));
28038         });
28039         if(this.buttons){
28040             this.add.apply(this, this.buttons);
28041             delete this.buttons;
28042         }
28043     },
28044
28045     /**
28046      * Adds element(s) to the toolbar -- this function takes a variable number of 
28047      * arguments of mixed type and adds them to the toolbar.
28048      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28049      * <ul>
28050      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28051      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28052      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28053      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28054      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28055      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28056      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28057      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28058      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28059      * </ul>
28060      * @param {Mixed} arg2
28061      * @param {Mixed} etc.
28062      */
28063     add : function(){
28064         var a = arguments, l = a.length;
28065         for(var i = 0; i < l; i++){
28066             this._add(a[i]);
28067         }
28068     },
28069     // private..
28070     _add : function(el) {
28071         
28072         if (el.xtype) {
28073             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28074         }
28075         
28076         if (el.applyTo){ // some kind of form field
28077             return this.addField(el);
28078         } 
28079         if (el.render){ // some kind of Toolbar.Item
28080             return this.addItem(el);
28081         }
28082         if (typeof el == "string"){ // string
28083             if(el == "separator" || el == "-"){
28084                 return this.addSeparator();
28085             }
28086             if (el == " "){
28087                 return this.addSpacer();
28088             }
28089             if(el == "->"){
28090                 return this.addFill();
28091             }
28092             return this.addText(el);
28093             
28094         }
28095         if(el.tagName){ // element
28096             return this.addElement(el);
28097         }
28098         if(typeof el == "object"){ // must be button config?
28099             return this.addButton(el);
28100         }
28101         // and now what?!?!
28102         return false;
28103         
28104     },
28105     
28106     /**
28107      * Add an Xtype element
28108      * @param {Object} xtype Xtype Object
28109      * @return {Object} created Object
28110      */
28111     addxtype : function(e){
28112         return this.add(e);  
28113     },
28114     
28115     /**
28116      * Returns the Element for this toolbar.
28117      * @return {Roo.Element}
28118      */
28119     getEl : function(){
28120         return this.el;  
28121     },
28122     
28123     /**
28124      * Adds a separator
28125      * @return {Roo.Toolbar.Item} The separator item
28126      */
28127     addSeparator : function(){
28128         return this.addItem(new Roo.Toolbar.Separator());
28129     },
28130
28131     /**
28132      * Adds a spacer element
28133      * @return {Roo.Toolbar.Spacer} The spacer item
28134      */
28135     addSpacer : function(){
28136         return this.addItem(new Roo.Toolbar.Spacer());
28137     },
28138
28139     /**
28140      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28141      * @return {Roo.Toolbar.Fill} The fill item
28142      */
28143     addFill : function(){
28144         return this.addItem(new Roo.Toolbar.Fill());
28145     },
28146
28147     /**
28148      * Adds any standard HTML element to the toolbar
28149      * @param {String/HTMLElement/Element} el The element or id of the element to add
28150      * @return {Roo.Toolbar.Item} The element's item
28151      */
28152     addElement : function(el){
28153         return this.addItem(new Roo.Toolbar.Item(el));
28154     },
28155     /**
28156      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28157      * @type Roo.util.MixedCollection  
28158      */
28159     items : false,
28160      
28161     /**
28162      * Adds any Toolbar.Item or subclass
28163      * @param {Roo.Toolbar.Item} item
28164      * @return {Roo.Toolbar.Item} The item
28165      */
28166     addItem : function(item){
28167         var td = this.nextBlock();
28168         item.render(td);
28169         this.items.add(item);
28170         return item;
28171     },
28172     
28173     /**
28174      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28175      * @param {Object/Array} config A button config or array of configs
28176      * @return {Roo.Toolbar.Button/Array}
28177      */
28178     addButton : function(config){
28179         if(config instanceof Array){
28180             var buttons = [];
28181             for(var i = 0, len = config.length; i < len; i++) {
28182                 buttons.push(this.addButton(config[i]));
28183             }
28184             return buttons;
28185         }
28186         var b = config;
28187         if(!(config instanceof Roo.Toolbar.Button)){
28188             b = config.split ?
28189                 new Roo.Toolbar.SplitButton(config) :
28190                 new Roo.Toolbar.Button(config);
28191         }
28192         var td = this.nextBlock();
28193         b.render(td);
28194         this.items.add(b);
28195         return b;
28196     },
28197     
28198     /**
28199      * Adds text to the toolbar
28200      * @param {String} text The text to add
28201      * @return {Roo.Toolbar.Item} The element's item
28202      */
28203     addText : function(text){
28204         return this.addItem(new Roo.Toolbar.TextItem(text));
28205     },
28206     
28207     /**
28208      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28209      * @param {Number} index The index where the item is to be inserted
28210      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28211      * @return {Roo.Toolbar.Button/Item}
28212      */
28213     insertButton : function(index, item){
28214         if(item instanceof Array){
28215             var buttons = [];
28216             for(var i = 0, len = item.length; i < len; i++) {
28217                buttons.push(this.insertButton(index + i, item[i]));
28218             }
28219             return buttons;
28220         }
28221         if (!(item instanceof Roo.Toolbar.Button)){
28222            item = new Roo.Toolbar.Button(item);
28223         }
28224         var td = document.createElement("td");
28225         this.tr.insertBefore(td, this.tr.childNodes[index]);
28226         item.render(td);
28227         this.items.insert(index, item);
28228         return item;
28229     },
28230     
28231     /**
28232      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28233      * @param {Object} config
28234      * @return {Roo.Toolbar.Item} The element's item
28235      */
28236     addDom : function(config, returnEl){
28237         var td = this.nextBlock();
28238         Roo.DomHelper.overwrite(td, config);
28239         var ti = new Roo.Toolbar.Item(td.firstChild);
28240         ti.render(td);
28241         this.items.add(ti);
28242         return ti;
28243     },
28244
28245     /**
28246      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28247      * @type Roo.util.MixedCollection  
28248      */
28249     fields : false,
28250     
28251     /**
28252      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28253      * Note: the field should not have been rendered yet. For a field that has already been
28254      * rendered, use {@link #addElement}.
28255      * @param {Roo.form.Field} field
28256      * @return {Roo.ToolbarItem}
28257      */
28258      
28259       
28260     addField : function(field) {
28261         if (!this.fields) {
28262             var autoId = 0;
28263             this.fields = new Roo.util.MixedCollection(false, function(o){
28264                 return o.id || ("item" + (++autoId));
28265             });
28266
28267         }
28268         
28269         var td = this.nextBlock();
28270         field.render(td);
28271         var ti = new Roo.Toolbar.Item(td.firstChild);
28272         ti.render(td);
28273         this.items.add(ti);
28274         this.fields.add(field);
28275         return ti;
28276     },
28277     /**
28278      * Hide the toolbar
28279      * @method hide
28280      */
28281      
28282       
28283     hide : function()
28284     {
28285         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28286         this.el.child('div').hide();
28287     },
28288     /**
28289      * Show the toolbar
28290      * @method show
28291      */
28292     show : function()
28293     {
28294         this.el.child('div').show();
28295     },
28296       
28297     // private
28298     nextBlock : function(){
28299         var td = document.createElement("td");
28300         this.tr.appendChild(td);
28301         return td;
28302     },
28303
28304     // private
28305     destroy : function(){
28306         if(this.items){ // rendered?
28307             Roo.destroy.apply(Roo, this.items.items);
28308         }
28309         if(this.fields){ // rendered?
28310             Roo.destroy.apply(Roo, this.fields.items);
28311         }
28312         Roo.Element.uncache(this.el, this.tr);
28313     }
28314 };
28315
28316 /**
28317  * @class Roo.Toolbar.Item
28318  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28319  * @constructor
28320  * Creates a new Item
28321  * @param {HTMLElement} el 
28322  */
28323 Roo.Toolbar.Item = function(el){
28324     this.el = Roo.getDom(el);
28325     this.id = Roo.id(this.el);
28326     this.hidden = false;
28327 };
28328
28329 Roo.Toolbar.Item.prototype = {
28330     
28331     /**
28332      * Get this item's HTML Element
28333      * @return {HTMLElement}
28334      */
28335     getEl : function(){
28336        return this.el;  
28337     },
28338
28339     // private
28340     render : function(td){
28341         this.td = td;
28342         td.appendChild(this.el);
28343     },
28344     
28345     /**
28346      * Removes and destroys this item.
28347      */
28348     destroy : function(){
28349         this.td.parentNode.removeChild(this.td);
28350     },
28351     
28352     /**
28353      * Shows this item.
28354      */
28355     show: function(){
28356         this.hidden = false;
28357         this.td.style.display = "";
28358     },
28359     
28360     /**
28361      * Hides this item.
28362      */
28363     hide: function(){
28364         this.hidden = true;
28365         this.td.style.display = "none";
28366     },
28367     
28368     /**
28369      * Convenience function for boolean show/hide.
28370      * @param {Boolean} visible true to show/false to hide
28371      */
28372     setVisible: function(visible){
28373         if(visible) {
28374             this.show();
28375         }else{
28376             this.hide();
28377         }
28378     },
28379     
28380     /**
28381      * Try to focus this item.
28382      */
28383     focus : function(){
28384         Roo.fly(this.el).focus();
28385     },
28386     
28387     /**
28388      * Disables this item.
28389      */
28390     disable : function(){
28391         Roo.fly(this.td).addClass("x-item-disabled");
28392         this.disabled = true;
28393         this.el.disabled = true;
28394     },
28395     
28396     /**
28397      * Enables this item.
28398      */
28399     enable : function(){
28400         Roo.fly(this.td).removeClass("x-item-disabled");
28401         this.disabled = false;
28402         this.el.disabled = false;
28403     }
28404 };
28405
28406
28407 /**
28408  * @class Roo.Toolbar.Separator
28409  * @extends Roo.Toolbar.Item
28410  * A simple toolbar separator class
28411  * @constructor
28412  * Creates a new Separator
28413  */
28414 Roo.Toolbar.Separator = function(){
28415     var s = document.createElement("span");
28416     s.className = "ytb-sep";
28417     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28418 };
28419 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28420     enable:Roo.emptyFn,
28421     disable:Roo.emptyFn,
28422     focus:Roo.emptyFn
28423 });
28424
28425 /**
28426  * @class Roo.Toolbar.Spacer
28427  * @extends Roo.Toolbar.Item
28428  * A simple element that adds extra horizontal space to a toolbar.
28429  * @constructor
28430  * Creates a new Spacer
28431  */
28432 Roo.Toolbar.Spacer = function(){
28433     var s = document.createElement("div");
28434     s.className = "ytb-spacer";
28435     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28436 };
28437 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28438     enable:Roo.emptyFn,
28439     disable:Roo.emptyFn,
28440     focus:Roo.emptyFn
28441 });
28442
28443 /**
28444  * @class Roo.Toolbar.Fill
28445  * @extends Roo.Toolbar.Spacer
28446  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28447  * @constructor
28448  * Creates a new Spacer
28449  */
28450 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28451     // private
28452     render : function(td){
28453         td.style.width = '100%';
28454         Roo.Toolbar.Fill.superclass.render.call(this, td);
28455     }
28456 });
28457
28458 /**
28459  * @class Roo.Toolbar.TextItem
28460  * @extends Roo.Toolbar.Item
28461  * A simple class that renders text directly into a toolbar.
28462  * @constructor
28463  * Creates a new TextItem
28464  * @param {String} text
28465  */
28466 Roo.Toolbar.TextItem = function(text){
28467     if (typeof(text) == 'object') {
28468         text = text.text;
28469     }
28470     var s = document.createElement("span");
28471     s.className = "ytb-text";
28472     s.innerHTML = text;
28473     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28474 };
28475 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28476     enable:Roo.emptyFn,
28477     disable:Roo.emptyFn,
28478     focus:Roo.emptyFn
28479 });
28480
28481 /**
28482  * @class Roo.Toolbar.Button
28483  * @extends Roo.Button
28484  * A button that renders into a toolbar.
28485  * @constructor
28486  * Creates a new Button
28487  * @param {Object} config A standard {@link Roo.Button} config object
28488  */
28489 Roo.Toolbar.Button = function(config){
28490     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28491 };
28492 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28493     render : function(td){
28494         this.td = td;
28495         Roo.Toolbar.Button.superclass.render.call(this, td);
28496     },
28497     
28498     /**
28499      * Removes and destroys this button
28500      */
28501     destroy : function(){
28502         Roo.Toolbar.Button.superclass.destroy.call(this);
28503         this.td.parentNode.removeChild(this.td);
28504     },
28505     
28506     /**
28507      * Shows this button
28508      */
28509     show: function(){
28510         this.hidden = false;
28511         this.td.style.display = "";
28512     },
28513     
28514     /**
28515      * Hides this button
28516      */
28517     hide: function(){
28518         this.hidden = true;
28519         this.td.style.display = "none";
28520     },
28521
28522     /**
28523      * Disables this item
28524      */
28525     disable : function(){
28526         Roo.fly(this.td).addClass("x-item-disabled");
28527         this.disabled = true;
28528     },
28529
28530     /**
28531      * Enables this item
28532      */
28533     enable : function(){
28534         Roo.fly(this.td).removeClass("x-item-disabled");
28535         this.disabled = false;
28536     }
28537 });
28538 // backwards compat
28539 Roo.ToolbarButton = Roo.Toolbar.Button;
28540
28541 /**
28542  * @class Roo.Toolbar.SplitButton
28543  * @extends Roo.SplitButton
28544  * A menu button that renders into a toolbar.
28545  * @constructor
28546  * Creates a new SplitButton
28547  * @param {Object} config A standard {@link Roo.SplitButton} config object
28548  */
28549 Roo.Toolbar.SplitButton = function(config){
28550     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28551 };
28552 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28553     render : function(td){
28554         this.td = td;
28555         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28556     },
28557     
28558     /**
28559      * Removes and destroys this button
28560      */
28561     destroy : function(){
28562         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28563         this.td.parentNode.removeChild(this.td);
28564     },
28565     
28566     /**
28567      * Shows this button
28568      */
28569     show: function(){
28570         this.hidden = false;
28571         this.td.style.display = "";
28572     },
28573     
28574     /**
28575      * Hides this button
28576      */
28577     hide: function(){
28578         this.hidden = true;
28579         this.td.style.display = "none";
28580     }
28581 });
28582
28583 // backwards compat
28584 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28585  * Based on:
28586  * Ext JS Library 1.1.1
28587  * Copyright(c) 2006-2007, Ext JS, LLC.
28588  *
28589  * Originally Released Under LGPL - original licence link has changed is not relivant.
28590  *
28591  * Fork - LGPL
28592  * <script type="text/javascript">
28593  */
28594  
28595 /**
28596  * @class Roo.PagingToolbar
28597  * @extends Roo.Toolbar
28598  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28599  * @constructor
28600  * Create a new PagingToolbar
28601  * @param {Object} config The config object
28602  */
28603 Roo.PagingToolbar = function(el, ds, config)
28604 {
28605     // old args format still supported... - xtype is prefered..
28606     if (typeof(el) == 'object' && el.xtype) {
28607         // created from xtype...
28608         config = el;
28609         ds = el.dataSource;
28610         el = config.container;
28611     }
28612     var items = [];
28613     if (config.items) {
28614         items = config.items;
28615         config.items = [];
28616     }
28617     
28618     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28619     this.ds = ds;
28620     this.cursor = 0;
28621     this.renderButtons(this.el);
28622     this.bind(ds);
28623     
28624     // supprot items array.
28625    
28626     Roo.each(items, function(e) {
28627         this.add(Roo.factory(e));
28628     },this);
28629     
28630 };
28631
28632 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28633     /**
28634      * @cfg {Roo.data.Store} dataSource
28635      * The underlying data store providing the paged data
28636      */
28637     /**
28638      * @cfg {String/HTMLElement/Element} container
28639      * container The id or element that will contain the toolbar
28640      */
28641     /**
28642      * @cfg {Boolean} displayInfo
28643      * True to display the displayMsg (defaults to false)
28644      */
28645     /**
28646      * @cfg {Number} pageSize
28647      * The number of records to display per page (defaults to 20)
28648      */
28649     pageSize: 20,
28650     /**
28651      * @cfg {String} displayMsg
28652      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28653      */
28654     displayMsg : 'Displaying {0} - {1} of {2}',
28655     /**
28656      * @cfg {String} emptyMsg
28657      * The message to display when no records are found (defaults to "No data to display")
28658      */
28659     emptyMsg : 'No data to display',
28660     /**
28661      * Customizable piece of the default paging text (defaults to "Page")
28662      * @type String
28663      */
28664     beforePageText : "Page",
28665     /**
28666      * Customizable piece of the default paging text (defaults to "of %0")
28667      * @type String
28668      */
28669     afterPageText : "of {0}",
28670     /**
28671      * Customizable piece of the default paging text (defaults to "First Page")
28672      * @type String
28673      */
28674     firstText : "First Page",
28675     /**
28676      * Customizable piece of the default paging text (defaults to "Previous Page")
28677      * @type String
28678      */
28679     prevText : "Previous Page",
28680     /**
28681      * Customizable piece of the default paging text (defaults to "Next Page")
28682      * @type String
28683      */
28684     nextText : "Next Page",
28685     /**
28686      * Customizable piece of the default paging text (defaults to "Last Page")
28687      * @type String
28688      */
28689     lastText : "Last Page",
28690     /**
28691      * Customizable piece of the default paging text (defaults to "Refresh")
28692      * @type String
28693      */
28694     refreshText : "Refresh",
28695
28696     // private
28697     renderButtons : function(el){
28698         Roo.PagingToolbar.superclass.render.call(this, el);
28699         this.first = this.addButton({
28700             tooltip: this.firstText,
28701             cls: "x-btn-icon x-grid-page-first",
28702             disabled: true,
28703             handler: this.onClick.createDelegate(this, ["first"])
28704         });
28705         this.prev = this.addButton({
28706             tooltip: this.prevText,
28707             cls: "x-btn-icon x-grid-page-prev",
28708             disabled: true,
28709             handler: this.onClick.createDelegate(this, ["prev"])
28710         });
28711         //this.addSeparator();
28712         this.add(this.beforePageText);
28713         this.field = Roo.get(this.addDom({
28714            tag: "input",
28715            type: "text",
28716            size: "3",
28717            value: "1",
28718            cls: "x-grid-page-number"
28719         }).el);
28720         this.field.on("keydown", this.onPagingKeydown, this);
28721         this.field.on("focus", function(){this.dom.select();});
28722         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28723         this.field.setHeight(18);
28724         //this.addSeparator();
28725         this.next = this.addButton({
28726             tooltip: this.nextText,
28727             cls: "x-btn-icon x-grid-page-next",
28728             disabled: true,
28729             handler: this.onClick.createDelegate(this, ["next"])
28730         });
28731         this.last = this.addButton({
28732             tooltip: this.lastText,
28733             cls: "x-btn-icon x-grid-page-last",
28734             disabled: true,
28735             handler: this.onClick.createDelegate(this, ["last"])
28736         });
28737         //this.addSeparator();
28738         this.loading = this.addButton({
28739             tooltip: this.refreshText,
28740             cls: "x-btn-icon x-grid-loading",
28741             handler: this.onClick.createDelegate(this, ["refresh"])
28742         });
28743
28744         if(this.displayInfo){
28745             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28746         }
28747     },
28748
28749     // private
28750     updateInfo : function(){
28751         if(this.displayEl){
28752             var count = this.ds.getCount();
28753             var msg = count == 0 ?
28754                 this.emptyMsg :
28755                 String.format(
28756                     this.displayMsg,
28757                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28758                 );
28759             this.displayEl.update(msg);
28760         }
28761     },
28762
28763     // private
28764     onLoad : function(ds, r, o){
28765        this.cursor = o.params ? o.params.start : 0;
28766        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28767
28768        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28769        this.field.dom.value = ap;
28770        this.first.setDisabled(ap == 1);
28771        this.prev.setDisabled(ap == 1);
28772        this.next.setDisabled(ap == ps);
28773        this.last.setDisabled(ap == ps);
28774        this.loading.enable();
28775        this.updateInfo();
28776     },
28777
28778     // private
28779     getPageData : function(){
28780         var total = this.ds.getTotalCount();
28781         return {
28782             total : total,
28783             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28784             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28785         };
28786     },
28787
28788     // private
28789     onLoadError : function(){
28790         this.loading.enable();
28791     },
28792
28793     // private
28794     onPagingKeydown : function(e){
28795         var k = e.getKey();
28796         var d = this.getPageData();
28797         if(k == e.RETURN){
28798             var v = this.field.dom.value, pageNum;
28799             if(!v || isNaN(pageNum = parseInt(v, 10))){
28800                 this.field.dom.value = d.activePage;
28801                 return;
28802             }
28803             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28804             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28805             e.stopEvent();
28806         }
28807         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))
28808         {
28809           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28810           this.field.dom.value = pageNum;
28811           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28812           e.stopEvent();
28813         }
28814         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28815         {
28816           var v = this.field.dom.value, pageNum; 
28817           var increment = (e.shiftKey) ? 10 : 1;
28818           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28819             increment *= -1;
28820           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28821             this.field.dom.value = d.activePage;
28822             return;
28823           }
28824           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28825           {
28826             this.field.dom.value = parseInt(v, 10) + increment;
28827             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28828             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28829           }
28830           e.stopEvent();
28831         }
28832     },
28833
28834     // private
28835     beforeLoad : function(){
28836         if(this.loading){
28837             this.loading.disable();
28838         }
28839     },
28840
28841     // private
28842     onClick : function(which){
28843         var ds = this.ds;
28844         switch(which){
28845             case "first":
28846                 ds.load({params:{start: 0, limit: this.pageSize}});
28847             break;
28848             case "prev":
28849                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28850             break;
28851             case "next":
28852                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28853             break;
28854             case "last":
28855                 var total = ds.getTotalCount();
28856                 var extra = total % this.pageSize;
28857                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28858                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28859             break;
28860             case "refresh":
28861                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28862             break;
28863         }
28864     },
28865
28866     /**
28867      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28868      * @param {Roo.data.Store} store The data store to unbind
28869      */
28870     unbind : function(ds){
28871         ds.un("beforeload", this.beforeLoad, this);
28872         ds.un("load", this.onLoad, this);
28873         ds.un("loadexception", this.onLoadError, this);
28874         ds.un("remove", this.updateInfo, this);
28875         ds.un("add", this.updateInfo, this);
28876         this.ds = undefined;
28877     },
28878
28879     /**
28880      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28881      * @param {Roo.data.Store} store The data store to bind
28882      */
28883     bind : function(ds){
28884         ds.on("beforeload", this.beforeLoad, this);
28885         ds.on("load", this.onLoad, this);
28886         ds.on("loadexception", this.onLoadError, this);
28887         ds.on("remove", this.updateInfo, this);
28888         ds.on("add", this.updateInfo, this);
28889         this.ds = ds;
28890     }
28891 });/*
28892  * Based on:
28893  * Ext JS Library 1.1.1
28894  * Copyright(c) 2006-2007, Ext JS, LLC.
28895  *
28896  * Originally Released Under LGPL - original licence link has changed is not relivant.
28897  *
28898  * Fork - LGPL
28899  * <script type="text/javascript">
28900  */
28901
28902 /**
28903  * @class Roo.Resizable
28904  * @extends Roo.util.Observable
28905  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28906  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28907  * 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
28908  * the element will be wrapped for you automatically.</p>
28909  * <p>Here is the list of valid resize handles:</p>
28910  * <pre>
28911 Value   Description
28912 ------  -------------------
28913  'n'     north
28914  's'     south
28915  'e'     east
28916  'w'     west
28917  'nw'    northwest
28918  'sw'    southwest
28919  'se'    southeast
28920  'ne'    northeast
28921  'hd'    horizontal drag
28922  'all'   all
28923 </pre>
28924  * <p>Here's an example showing the creation of a typical Resizable:</p>
28925  * <pre><code>
28926 var resizer = new Roo.Resizable("element-id", {
28927     handles: 'all',
28928     minWidth: 200,
28929     minHeight: 100,
28930     maxWidth: 500,
28931     maxHeight: 400,
28932     pinned: true
28933 });
28934 resizer.on("resize", myHandler);
28935 </code></pre>
28936  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28937  * resizer.east.setDisplayed(false);</p>
28938  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28939  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28940  * resize operation's new size (defaults to [0, 0])
28941  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28942  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28943  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28944  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28945  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28946  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28947  * @cfg {Number} width The width of the element in pixels (defaults to null)
28948  * @cfg {Number} height The height of the element in pixels (defaults to null)
28949  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28950  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28951  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28952  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28953  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28954  * in favor of the handles config option (defaults to false)
28955  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28956  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28957  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28958  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28959  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28960  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28961  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28962  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28963  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28964  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28965  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28966  * @constructor
28967  * Create a new resizable component
28968  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28969  * @param {Object} config configuration options
28970   */
28971 Roo.Resizable = function(el, config)
28972 {
28973     this.el = Roo.get(el);
28974
28975     if(config && config.wrap){
28976         config.resizeChild = this.el;
28977         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28978         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28979         this.el.setStyle("overflow", "hidden");
28980         this.el.setPositioning(config.resizeChild.getPositioning());
28981         config.resizeChild.clearPositioning();
28982         if(!config.width || !config.height){
28983             var csize = config.resizeChild.getSize();
28984             this.el.setSize(csize.width, csize.height);
28985         }
28986         if(config.pinned && !config.adjustments){
28987             config.adjustments = "auto";
28988         }
28989     }
28990
28991     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28992     this.proxy.unselectable();
28993     this.proxy.enableDisplayMode('block');
28994
28995     Roo.apply(this, config);
28996
28997     if(this.pinned){
28998         this.disableTrackOver = true;
28999         this.el.addClass("x-resizable-pinned");
29000     }
29001     // if the element isn't positioned, make it relative
29002     var position = this.el.getStyle("position");
29003     if(position != "absolute" && position != "fixed"){
29004         this.el.setStyle("position", "relative");
29005     }
29006     if(!this.handles){ // no handles passed, must be legacy style
29007         this.handles = 's,e,se';
29008         if(this.multiDirectional){
29009             this.handles += ',n,w';
29010         }
29011     }
29012     if(this.handles == "all"){
29013         this.handles = "n s e w ne nw se sw";
29014     }
29015     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29016     var ps = Roo.Resizable.positions;
29017     for(var i = 0, len = hs.length; i < len; i++){
29018         if(hs[i] && ps[hs[i]]){
29019             var pos = ps[hs[i]];
29020             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29021         }
29022     }
29023     // legacy
29024     this.corner = this.southeast;
29025     
29026     // updateBox = the box can move..
29027     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29028         this.updateBox = true;
29029     }
29030
29031     this.activeHandle = null;
29032
29033     if(this.resizeChild){
29034         if(typeof this.resizeChild == "boolean"){
29035             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29036         }else{
29037             this.resizeChild = Roo.get(this.resizeChild, true);
29038         }
29039     }
29040     
29041     if(this.adjustments == "auto"){
29042         var rc = this.resizeChild;
29043         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29044         if(rc && (hw || hn)){
29045             rc.position("relative");
29046             rc.setLeft(hw ? hw.el.getWidth() : 0);
29047             rc.setTop(hn ? hn.el.getHeight() : 0);
29048         }
29049         this.adjustments = [
29050             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29051             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29052         ];
29053     }
29054
29055     if(this.draggable){
29056         this.dd = this.dynamic ?
29057             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29058         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29059     }
29060
29061     // public events
29062     this.addEvents({
29063         /**
29064          * @event beforeresize
29065          * Fired before resize is allowed. Set enabled to false to cancel resize.
29066          * @param {Roo.Resizable} this
29067          * @param {Roo.EventObject} e The mousedown event
29068          */
29069         "beforeresize" : true,
29070         /**
29071          * @event resizing
29072          * Fired a resizing.
29073          * @param {Roo.Resizable} this
29074          * @param {Number} x The new x position
29075          * @param {Number} y The new y position
29076          * @param {Number} w The new w width
29077          * @param {Number} h The new h hight
29078          * @param {Roo.EventObject} e The mouseup event
29079          */
29080         "resizing" : true,
29081         /**
29082          * @event resize
29083          * Fired after a resize.
29084          * @param {Roo.Resizable} this
29085          * @param {Number} width The new width
29086          * @param {Number} height The new height
29087          * @param {Roo.EventObject} e The mouseup event
29088          */
29089         "resize" : true
29090     });
29091
29092     if(this.width !== null && this.height !== null){
29093         this.resizeTo(this.width, this.height);
29094     }else{
29095         this.updateChildSize();
29096     }
29097     if(Roo.isIE){
29098         this.el.dom.style.zoom = 1;
29099     }
29100     Roo.Resizable.superclass.constructor.call(this);
29101 };
29102
29103 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29104         resizeChild : false,
29105         adjustments : [0, 0],
29106         minWidth : 5,
29107         minHeight : 5,
29108         maxWidth : 10000,
29109         maxHeight : 10000,
29110         enabled : true,
29111         animate : false,
29112         duration : .35,
29113         dynamic : false,
29114         handles : false,
29115         multiDirectional : false,
29116         disableTrackOver : false,
29117         easing : 'easeOutStrong',
29118         widthIncrement : 0,
29119         heightIncrement : 0,
29120         pinned : false,
29121         width : null,
29122         height : null,
29123         preserveRatio : false,
29124         transparent: false,
29125         minX: 0,
29126         minY: 0,
29127         draggable: false,
29128
29129         /**
29130          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29131          */
29132         constrainTo: undefined,
29133         /**
29134          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29135          */
29136         resizeRegion: undefined,
29137
29138
29139     /**
29140      * Perform a manual resize
29141      * @param {Number} width
29142      * @param {Number} height
29143      */
29144     resizeTo : function(width, height){
29145         this.el.setSize(width, height);
29146         this.updateChildSize();
29147         this.fireEvent("resize", this, width, height, null);
29148     },
29149
29150     // private
29151     startSizing : function(e, handle){
29152         this.fireEvent("beforeresize", this, e);
29153         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29154
29155             if(!this.overlay){
29156                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29157                 this.overlay.unselectable();
29158                 this.overlay.enableDisplayMode("block");
29159                 this.overlay.on("mousemove", this.onMouseMove, this);
29160                 this.overlay.on("mouseup", this.onMouseUp, this);
29161             }
29162             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29163
29164             this.resizing = true;
29165             this.startBox = this.el.getBox();
29166             this.startPoint = e.getXY();
29167             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29168                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29169
29170             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29171             this.overlay.show();
29172
29173             if(this.constrainTo) {
29174                 var ct = Roo.get(this.constrainTo);
29175                 this.resizeRegion = ct.getRegion().adjust(
29176                     ct.getFrameWidth('t'),
29177                     ct.getFrameWidth('l'),
29178                     -ct.getFrameWidth('b'),
29179                     -ct.getFrameWidth('r')
29180                 );
29181             }
29182
29183             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29184             this.proxy.show();
29185             this.proxy.setBox(this.startBox);
29186             if(!this.dynamic){
29187                 this.proxy.setStyle('visibility', 'visible');
29188             }
29189         }
29190     },
29191
29192     // private
29193     onMouseDown : function(handle, e){
29194         if(this.enabled){
29195             e.stopEvent();
29196             this.activeHandle = handle;
29197             this.startSizing(e, handle);
29198         }
29199     },
29200
29201     // private
29202     onMouseUp : function(e){
29203         var size = this.resizeElement();
29204         this.resizing = false;
29205         this.handleOut();
29206         this.overlay.hide();
29207         this.proxy.hide();
29208         this.fireEvent("resize", this, size.width, size.height, e);
29209     },
29210
29211     // private
29212     updateChildSize : function(){
29213         
29214         if(this.resizeChild){
29215             var el = this.el;
29216             var child = this.resizeChild;
29217             var adj = this.adjustments;
29218             if(el.dom.offsetWidth){
29219                 var b = el.getSize(true);
29220                 child.setSize(b.width+adj[0], b.height+adj[1]);
29221             }
29222             // Second call here for IE
29223             // The first call enables instant resizing and
29224             // the second call corrects scroll bars if they
29225             // exist
29226             if(Roo.isIE){
29227                 setTimeout(function(){
29228                     if(el.dom.offsetWidth){
29229                         var b = el.getSize(true);
29230                         child.setSize(b.width+adj[0], b.height+adj[1]);
29231                     }
29232                 }, 10);
29233             }
29234         }
29235     },
29236
29237     // private
29238     snap : function(value, inc, min){
29239         if(!inc || !value) return value;
29240         var newValue = value;
29241         var m = value % inc;
29242         if(m > 0){
29243             if(m > (inc/2)){
29244                 newValue = value + (inc-m);
29245             }else{
29246                 newValue = value - m;
29247             }
29248         }
29249         return Math.max(min, newValue);
29250     },
29251
29252     // private
29253     resizeElement : function(){
29254         var box = this.proxy.getBox();
29255         if(this.updateBox){
29256             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29257         }else{
29258             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29259         }
29260         this.updateChildSize();
29261         if(!this.dynamic){
29262             this.proxy.hide();
29263         }
29264         return box;
29265     },
29266
29267     // private
29268     constrain : function(v, diff, m, mx){
29269         if(v - diff < m){
29270             diff = v - m;
29271         }else if(v - diff > mx){
29272             diff = mx - v;
29273         }
29274         return diff;
29275     },
29276
29277     // private
29278     onMouseMove : function(e){
29279         
29280         if(this.enabled){
29281             try{// try catch so if something goes wrong the user doesn't get hung
29282
29283             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29284                 return;
29285             }
29286
29287             //var curXY = this.startPoint;
29288             var curSize = this.curSize || this.startBox;
29289             var x = this.startBox.x, y = this.startBox.y;
29290             var ox = x, oy = y;
29291             var w = curSize.width, h = curSize.height;
29292             var ow = w, oh = h;
29293             var mw = this.minWidth, mh = this.minHeight;
29294             var mxw = this.maxWidth, mxh = this.maxHeight;
29295             var wi = this.widthIncrement;
29296             var hi = this.heightIncrement;
29297
29298             var eventXY = e.getXY();
29299             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29300             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29301
29302             var pos = this.activeHandle.position;
29303
29304             switch(pos){
29305                 case "east":
29306                     w += diffX;
29307                     w = Math.min(Math.max(mw, w), mxw);
29308                     break;
29309              
29310                 case "south":
29311                     h += diffY;
29312                     h = Math.min(Math.max(mh, h), mxh);
29313                     break;
29314                 case "southeast":
29315                     w += diffX;
29316                     h += diffY;
29317                     w = Math.min(Math.max(mw, w), mxw);
29318                     h = Math.min(Math.max(mh, h), mxh);
29319                     break;
29320                 case "north":
29321                     diffY = this.constrain(h, diffY, mh, mxh);
29322                     y += diffY;
29323                     h -= diffY;
29324                     break;
29325                 case "hdrag":
29326                     
29327                     if (wi) {
29328                         var adiffX = Math.abs(diffX);
29329                         var sub = (adiffX % wi); // how much 
29330                         if (sub > (wi/2)) { // far enough to snap
29331                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29332                         } else {
29333                             // remove difference.. 
29334                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29335                         }
29336                     }
29337                     x += diffX;
29338                     x = Math.max(this.minX, x);
29339                     break;
29340                 case "west":
29341                     diffX = this.constrain(w, diffX, mw, mxw);
29342                     x += diffX;
29343                     w -= diffX;
29344                     break;
29345                 case "northeast":
29346                     w += diffX;
29347                     w = Math.min(Math.max(mw, w), mxw);
29348                     diffY = this.constrain(h, diffY, mh, mxh);
29349                     y += diffY;
29350                     h -= diffY;
29351                     break;
29352                 case "northwest":
29353                     diffX = this.constrain(w, diffX, mw, mxw);
29354                     diffY = this.constrain(h, diffY, mh, mxh);
29355                     y += diffY;
29356                     h -= diffY;
29357                     x += diffX;
29358                     w -= diffX;
29359                     break;
29360                case "southwest":
29361                     diffX = this.constrain(w, diffX, mw, mxw);
29362                     h += diffY;
29363                     h = Math.min(Math.max(mh, h), mxh);
29364                     x += diffX;
29365                     w -= diffX;
29366                     break;
29367             }
29368
29369             var sw = this.snap(w, wi, mw);
29370             var sh = this.snap(h, hi, mh);
29371             if(sw != w || sh != h){
29372                 switch(pos){
29373                     case "northeast":
29374                         y -= sh - h;
29375                     break;
29376                     case "north":
29377                         y -= sh - h;
29378                         break;
29379                     case "southwest":
29380                         x -= sw - w;
29381                     break;
29382                     case "west":
29383                         x -= sw - w;
29384                         break;
29385                     case "northwest":
29386                         x -= sw - w;
29387                         y -= sh - h;
29388                     break;
29389                 }
29390                 w = sw;
29391                 h = sh;
29392             }
29393
29394             if(this.preserveRatio){
29395                 switch(pos){
29396                     case "southeast":
29397                     case "east":
29398                         h = oh * (w/ow);
29399                         h = Math.min(Math.max(mh, h), mxh);
29400                         w = ow * (h/oh);
29401                        break;
29402                     case "south":
29403                         w = ow * (h/oh);
29404                         w = Math.min(Math.max(mw, w), mxw);
29405                         h = oh * (w/ow);
29406                         break;
29407                     case "northeast":
29408                         w = ow * (h/oh);
29409                         w = Math.min(Math.max(mw, w), mxw);
29410                         h = oh * (w/ow);
29411                     break;
29412                     case "north":
29413                         var tw = w;
29414                         w = ow * (h/oh);
29415                         w = Math.min(Math.max(mw, w), mxw);
29416                         h = oh * (w/ow);
29417                         x += (tw - w) / 2;
29418                         break;
29419                     case "southwest":
29420                         h = oh * (w/ow);
29421                         h = Math.min(Math.max(mh, h), mxh);
29422                         var tw = w;
29423                         w = ow * (h/oh);
29424                         x += tw - w;
29425                         break;
29426                     case "west":
29427                         var th = h;
29428                         h = oh * (w/ow);
29429                         h = Math.min(Math.max(mh, h), mxh);
29430                         y += (th - h) / 2;
29431                         var tw = w;
29432                         w = ow * (h/oh);
29433                         x += tw - w;
29434                        break;
29435                     case "northwest":
29436                         var tw = w;
29437                         var th = h;
29438                         h = oh * (w/ow);
29439                         h = Math.min(Math.max(mh, h), mxh);
29440                         w = ow * (h/oh);
29441                         y += th - h;
29442                         x += tw - w;
29443                        break;
29444
29445                 }
29446             }
29447             if (pos == 'hdrag') {
29448                 w = ow;
29449             }
29450             this.proxy.setBounds(x, y, w, h);
29451             if(this.dynamic){
29452                 this.resizeElement();
29453             }
29454             }catch(e){}
29455         }
29456         this.fireEvent("resizing", this, x, y, w, h, e);
29457     },
29458
29459     // private
29460     handleOver : function(){
29461         if(this.enabled){
29462             this.el.addClass("x-resizable-over");
29463         }
29464     },
29465
29466     // private
29467     handleOut : function(){
29468         if(!this.resizing){
29469             this.el.removeClass("x-resizable-over");
29470         }
29471     },
29472
29473     /**
29474      * Returns the element this component is bound to.
29475      * @return {Roo.Element}
29476      */
29477     getEl : function(){
29478         return this.el;
29479     },
29480
29481     /**
29482      * Returns the resizeChild element (or null).
29483      * @return {Roo.Element}
29484      */
29485     getResizeChild : function(){
29486         return this.resizeChild;
29487     },
29488     groupHandler : function()
29489     {
29490         
29491     },
29492     /**
29493      * Destroys this resizable. If the element was wrapped and
29494      * removeEl is not true then the element remains.
29495      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29496      */
29497     destroy : function(removeEl){
29498         this.proxy.remove();
29499         if(this.overlay){
29500             this.overlay.removeAllListeners();
29501             this.overlay.remove();
29502         }
29503         var ps = Roo.Resizable.positions;
29504         for(var k in ps){
29505             if(typeof ps[k] != "function" && this[ps[k]]){
29506                 var h = this[ps[k]];
29507                 h.el.removeAllListeners();
29508                 h.el.remove();
29509             }
29510         }
29511         if(removeEl){
29512             this.el.update("");
29513             this.el.remove();
29514         }
29515     }
29516 });
29517
29518 // private
29519 // hash to map config positions to true positions
29520 Roo.Resizable.positions = {
29521     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29522     hd: "hdrag"
29523 };
29524
29525 // private
29526 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29527     if(!this.tpl){
29528         // only initialize the template if resizable is used
29529         var tpl = Roo.DomHelper.createTemplate(
29530             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29531         );
29532         tpl.compile();
29533         Roo.Resizable.Handle.prototype.tpl = tpl;
29534     }
29535     this.position = pos;
29536     this.rz = rz;
29537     // show north drag fro topdra
29538     var handlepos = pos == 'hdrag' ? 'north' : pos;
29539     
29540     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29541     if (pos == 'hdrag') {
29542         this.el.setStyle('cursor', 'pointer');
29543     }
29544     this.el.unselectable();
29545     if(transparent){
29546         this.el.setOpacity(0);
29547     }
29548     this.el.on("mousedown", this.onMouseDown, this);
29549     if(!disableTrackOver){
29550         this.el.on("mouseover", this.onMouseOver, this);
29551         this.el.on("mouseout", this.onMouseOut, this);
29552     }
29553 };
29554
29555 // private
29556 Roo.Resizable.Handle.prototype = {
29557     afterResize : function(rz){
29558         // do nothing
29559     },
29560     // private
29561     onMouseDown : function(e){
29562         this.rz.onMouseDown(this, e);
29563     },
29564     // private
29565     onMouseOver : function(e){
29566         this.rz.handleOver(this, e);
29567     },
29568     // private
29569     onMouseOut : function(e){
29570         this.rz.handleOut(this, e);
29571     }
29572 };/*
29573  * Based on:
29574  * Ext JS Library 1.1.1
29575  * Copyright(c) 2006-2007, Ext JS, LLC.
29576  *
29577  * Originally Released Under LGPL - original licence link has changed is not relivant.
29578  *
29579  * Fork - LGPL
29580  * <script type="text/javascript">
29581  */
29582
29583 /**
29584  * @class Roo.Editor
29585  * @extends Roo.Component
29586  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29587  * @constructor
29588  * Create a new Editor
29589  * @param {Roo.form.Field} field The Field object (or descendant)
29590  * @param {Object} config The config object
29591  */
29592 Roo.Editor = function(field, config){
29593     Roo.Editor.superclass.constructor.call(this, config);
29594     this.field = field;
29595     this.addEvents({
29596         /**
29597              * @event beforestartedit
29598              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29599              * false from the handler of this event.
29600              * @param {Editor} this
29601              * @param {Roo.Element} boundEl The underlying element bound to this editor
29602              * @param {Mixed} value The field value being set
29603              */
29604         "beforestartedit" : true,
29605         /**
29606              * @event startedit
29607              * Fires when this editor is displayed
29608              * @param {Roo.Element} boundEl The underlying element bound to this editor
29609              * @param {Mixed} value The starting field value
29610              */
29611         "startedit" : true,
29612         /**
29613              * @event beforecomplete
29614              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29615              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29616              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29617              * event will not fire since no edit actually occurred.
29618              * @param {Editor} this
29619              * @param {Mixed} value The current field value
29620              * @param {Mixed} startValue The original field value
29621              */
29622         "beforecomplete" : true,
29623         /**
29624              * @event complete
29625              * Fires after editing is complete and any changed value has been written to the underlying field.
29626              * @param {Editor} this
29627              * @param {Mixed} value The current field value
29628              * @param {Mixed} startValue The original field value
29629              */
29630         "complete" : true,
29631         /**
29632          * @event specialkey
29633          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29634          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29635          * @param {Roo.form.Field} this
29636          * @param {Roo.EventObject} e The event object
29637          */
29638         "specialkey" : true
29639     });
29640 };
29641
29642 Roo.extend(Roo.Editor, Roo.Component, {
29643     /**
29644      * @cfg {Boolean/String} autosize
29645      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29646      * or "height" to adopt the height only (defaults to false)
29647      */
29648     /**
29649      * @cfg {Boolean} revertInvalid
29650      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29651      * validation fails (defaults to true)
29652      */
29653     /**
29654      * @cfg {Boolean} ignoreNoChange
29655      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29656      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29657      * will never be ignored.
29658      */
29659     /**
29660      * @cfg {Boolean} hideEl
29661      * False to keep the bound element visible while the editor is displayed (defaults to true)
29662      */
29663     /**
29664      * @cfg {Mixed} value
29665      * The data value of the underlying field (defaults to "")
29666      */
29667     value : "",
29668     /**
29669      * @cfg {String} alignment
29670      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29671      */
29672     alignment: "c-c?",
29673     /**
29674      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29675      * for bottom-right shadow (defaults to "frame")
29676      */
29677     shadow : "frame",
29678     /**
29679      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29680      */
29681     constrain : false,
29682     /**
29683      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29684      */
29685     completeOnEnter : false,
29686     /**
29687      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29688      */
29689     cancelOnEsc : false,
29690     /**
29691      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29692      */
29693     updateEl : false,
29694
29695     // private
29696     onRender : function(ct, position){
29697         this.el = new Roo.Layer({
29698             shadow: this.shadow,
29699             cls: "x-editor",
29700             parentEl : ct,
29701             shim : this.shim,
29702             shadowOffset:4,
29703             id: this.id,
29704             constrain: this.constrain
29705         });
29706         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29707         if(this.field.msgTarget != 'title'){
29708             this.field.msgTarget = 'qtip';
29709         }
29710         this.field.render(this.el);
29711         if(Roo.isGecko){
29712             this.field.el.dom.setAttribute('autocomplete', 'off');
29713         }
29714         this.field.on("specialkey", this.onSpecialKey, this);
29715         if(this.swallowKeys){
29716             this.field.el.swallowEvent(['keydown','keypress']);
29717         }
29718         this.field.show();
29719         this.field.on("blur", this.onBlur, this);
29720         if(this.field.grow){
29721             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29722         }
29723     },
29724
29725     onSpecialKey : function(field, e)
29726     {
29727         //Roo.log('editor onSpecialKey');
29728         if(this.completeOnEnter && e.getKey() == e.ENTER){
29729             e.stopEvent();
29730             this.completeEdit();
29731             return;
29732         }
29733         // do not fire special key otherwise it might hide close the editor...
29734         if(e.getKey() == e.ENTER){    
29735             return;
29736         }
29737         if(this.cancelOnEsc && e.getKey() == e.ESC){
29738             this.cancelEdit();
29739             return;
29740         } 
29741         this.fireEvent('specialkey', field, e);
29742     
29743     },
29744
29745     /**
29746      * Starts the editing process and shows the editor.
29747      * @param {String/HTMLElement/Element} el The element to edit
29748      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29749       * to the innerHTML of el.
29750      */
29751     startEdit : function(el, value){
29752         if(this.editing){
29753             this.completeEdit();
29754         }
29755         this.boundEl = Roo.get(el);
29756         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29757         if(!this.rendered){
29758             this.render(this.parentEl || document.body);
29759         }
29760         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29761             return;
29762         }
29763         this.startValue = v;
29764         this.field.setValue(v);
29765         if(this.autoSize){
29766             var sz = this.boundEl.getSize();
29767             switch(this.autoSize){
29768                 case "width":
29769                 this.setSize(sz.width,  "");
29770                 break;
29771                 case "height":
29772                 this.setSize("",  sz.height);
29773                 break;
29774                 default:
29775                 this.setSize(sz.width,  sz.height);
29776             }
29777         }
29778         this.el.alignTo(this.boundEl, this.alignment);
29779         this.editing = true;
29780         if(Roo.QuickTips){
29781             Roo.QuickTips.disable();
29782         }
29783         this.show();
29784     },
29785
29786     /**
29787      * Sets the height and width of this editor.
29788      * @param {Number} width The new width
29789      * @param {Number} height The new height
29790      */
29791     setSize : function(w, h){
29792         this.field.setSize(w, h);
29793         if(this.el){
29794             this.el.sync();
29795         }
29796     },
29797
29798     /**
29799      * Realigns the editor to the bound field based on the current alignment config value.
29800      */
29801     realign : function(){
29802         this.el.alignTo(this.boundEl, this.alignment);
29803     },
29804
29805     /**
29806      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29807      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29808      */
29809     completeEdit : function(remainVisible){
29810         if(!this.editing){
29811             return;
29812         }
29813         var v = this.getValue();
29814         if(this.revertInvalid !== false && !this.field.isValid()){
29815             v = this.startValue;
29816             this.cancelEdit(true);
29817         }
29818         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29819             this.editing = false;
29820             this.hide();
29821             return;
29822         }
29823         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29824             this.editing = false;
29825             if(this.updateEl && this.boundEl){
29826                 this.boundEl.update(v);
29827             }
29828             if(remainVisible !== true){
29829                 this.hide();
29830             }
29831             this.fireEvent("complete", this, v, this.startValue);
29832         }
29833     },
29834
29835     // private
29836     onShow : function(){
29837         this.el.show();
29838         if(this.hideEl !== false){
29839             this.boundEl.hide();
29840         }
29841         this.field.show();
29842         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29843             this.fixIEFocus = true;
29844             this.deferredFocus.defer(50, this);
29845         }else{
29846             this.field.focus();
29847         }
29848         this.fireEvent("startedit", this.boundEl, this.startValue);
29849     },
29850
29851     deferredFocus : function(){
29852         if(this.editing){
29853             this.field.focus();
29854         }
29855     },
29856
29857     /**
29858      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29859      * reverted to the original starting value.
29860      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29861      * cancel (defaults to false)
29862      */
29863     cancelEdit : function(remainVisible){
29864         if(this.editing){
29865             this.setValue(this.startValue);
29866             if(remainVisible !== true){
29867                 this.hide();
29868             }
29869         }
29870     },
29871
29872     // private
29873     onBlur : function(){
29874         if(this.allowBlur !== true && this.editing){
29875             this.completeEdit();
29876         }
29877     },
29878
29879     // private
29880     onHide : function(){
29881         if(this.editing){
29882             this.completeEdit();
29883             return;
29884         }
29885         this.field.blur();
29886         if(this.field.collapse){
29887             this.field.collapse();
29888         }
29889         this.el.hide();
29890         if(this.hideEl !== false){
29891             this.boundEl.show();
29892         }
29893         if(Roo.QuickTips){
29894             Roo.QuickTips.enable();
29895         }
29896     },
29897
29898     /**
29899      * Sets the data value of the editor
29900      * @param {Mixed} value Any valid value supported by the underlying field
29901      */
29902     setValue : function(v){
29903         this.field.setValue(v);
29904     },
29905
29906     /**
29907      * Gets the data value of the editor
29908      * @return {Mixed} The data value
29909      */
29910     getValue : function(){
29911         return this.field.getValue();
29912     }
29913 });/*
29914  * Based on:
29915  * Ext JS Library 1.1.1
29916  * Copyright(c) 2006-2007, Ext JS, LLC.
29917  *
29918  * Originally Released Under LGPL - original licence link has changed is not relivant.
29919  *
29920  * Fork - LGPL
29921  * <script type="text/javascript">
29922  */
29923  
29924 /**
29925  * @class Roo.BasicDialog
29926  * @extends Roo.util.Observable
29927  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29928  * <pre><code>
29929 var dlg = new Roo.BasicDialog("my-dlg", {
29930     height: 200,
29931     width: 300,
29932     minHeight: 100,
29933     minWidth: 150,
29934     modal: true,
29935     proxyDrag: true,
29936     shadow: true
29937 });
29938 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29939 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29940 dlg.addButton('Cancel', dlg.hide, dlg);
29941 dlg.show();
29942 </code></pre>
29943   <b>A Dialog should always be a direct child of the body element.</b>
29944  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29945  * @cfg {String} title Default text to display in the title bar (defaults to null)
29946  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29947  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29948  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29949  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29950  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29951  * (defaults to null with no animation)
29952  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29953  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29954  * property for valid values (defaults to 'all')
29955  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29956  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29957  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29958  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29959  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29960  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29961  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29962  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29963  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29964  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29965  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29966  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29967  * draggable = true (defaults to false)
29968  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29969  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29970  * shadow (defaults to false)
29971  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29972  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29973  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29974  * @cfg {Array} buttons Array of buttons
29975  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29976  * @constructor
29977  * Create a new BasicDialog.
29978  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29979  * @param {Object} config Configuration options
29980  */
29981 Roo.BasicDialog = function(el, config){
29982     this.el = Roo.get(el);
29983     var dh = Roo.DomHelper;
29984     if(!this.el && config && config.autoCreate){
29985         if(typeof config.autoCreate == "object"){
29986             if(!config.autoCreate.id){
29987                 config.autoCreate.id = el;
29988             }
29989             this.el = dh.append(document.body,
29990                         config.autoCreate, true);
29991         }else{
29992             this.el = dh.append(document.body,
29993                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29994         }
29995     }
29996     el = this.el;
29997     el.setDisplayed(true);
29998     el.hide = this.hideAction;
29999     this.id = el.id;
30000     el.addClass("x-dlg");
30001
30002     Roo.apply(this, config);
30003
30004     this.proxy = el.createProxy("x-dlg-proxy");
30005     this.proxy.hide = this.hideAction;
30006     this.proxy.setOpacity(.5);
30007     this.proxy.hide();
30008
30009     if(config.width){
30010         el.setWidth(config.width);
30011     }
30012     if(config.height){
30013         el.setHeight(config.height);
30014     }
30015     this.size = el.getSize();
30016     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30017         this.xy = [config.x,config.y];
30018     }else{
30019         this.xy = el.getCenterXY(true);
30020     }
30021     /** The header element @type Roo.Element */
30022     this.header = el.child("> .x-dlg-hd");
30023     /** The body element @type Roo.Element */
30024     this.body = el.child("> .x-dlg-bd");
30025     /** The footer element @type Roo.Element */
30026     this.footer = el.child("> .x-dlg-ft");
30027
30028     if(!this.header){
30029         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30030     }
30031     if(!this.body){
30032         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30033     }
30034
30035     this.header.unselectable();
30036     if(this.title){
30037         this.header.update(this.title);
30038     }
30039     // this element allows the dialog to be focused for keyboard event
30040     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30041     this.focusEl.swallowEvent("click", true);
30042
30043     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30044
30045     // wrap the body and footer for special rendering
30046     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30047     if(this.footer){
30048         this.bwrap.dom.appendChild(this.footer.dom);
30049     }
30050
30051     this.bg = this.el.createChild({
30052         tag: "div", cls:"x-dlg-bg",
30053         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30054     });
30055     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30056
30057
30058     if(this.autoScroll !== false && !this.autoTabs){
30059         this.body.setStyle("overflow", "auto");
30060     }
30061
30062     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30063
30064     if(this.closable !== false){
30065         this.el.addClass("x-dlg-closable");
30066         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30067         this.close.on("click", this.closeClick, this);
30068         this.close.addClassOnOver("x-dlg-close-over");
30069     }
30070     if(this.collapsible !== false){
30071         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30072         this.collapseBtn.on("click", this.collapseClick, this);
30073         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30074         this.header.on("dblclick", this.collapseClick, this);
30075     }
30076     if(this.resizable !== false){
30077         this.el.addClass("x-dlg-resizable");
30078         this.resizer = new Roo.Resizable(el, {
30079             minWidth: this.minWidth || 80,
30080             minHeight:this.minHeight || 80,
30081             handles: this.resizeHandles || "all",
30082             pinned: true
30083         });
30084         this.resizer.on("beforeresize", this.beforeResize, this);
30085         this.resizer.on("resize", this.onResize, this);
30086     }
30087     if(this.draggable !== false){
30088         el.addClass("x-dlg-draggable");
30089         if (!this.proxyDrag) {
30090             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30091         }
30092         else {
30093             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30094         }
30095         dd.setHandleElId(this.header.id);
30096         dd.endDrag = this.endMove.createDelegate(this);
30097         dd.startDrag = this.startMove.createDelegate(this);
30098         dd.onDrag = this.onDrag.createDelegate(this);
30099         dd.scroll = false;
30100         this.dd = dd;
30101     }
30102     if(this.modal){
30103         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30104         this.mask.enableDisplayMode("block");
30105         this.mask.hide();
30106         this.el.addClass("x-dlg-modal");
30107     }
30108     if(this.shadow){
30109         this.shadow = new Roo.Shadow({
30110             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30111             offset : this.shadowOffset
30112         });
30113     }else{
30114         this.shadowOffset = 0;
30115     }
30116     if(Roo.useShims && this.shim !== false){
30117         this.shim = this.el.createShim();
30118         this.shim.hide = this.hideAction;
30119         this.shim.hide();
30120     }else{
30121         this.shim = false;
30122     }
30123     if(this.autoTabs){
30124         this.initTabs();
30125     }
30126     if (this.buttons) { 
30127         var bts= this.buttons;
30128         this.buttons = [];
30129         Roo.each(bts, function(b) {
30130             this.addButton(b);
30131         }, this);
30132     }
30133     
30134     
30135     this.addEvents({
30136         /**
30137          * @event keydown
30138          * Fires when a key is pressed
30139          * @param {Roo.BasicDialog} this
30140          * @param {Roo.EventObject} e
30141          */
30142         "keydown" : true,
30143         /**
30144          * @event move
30145          * Fires when this dialog is moved by the user.
30146          * @param {Roo.BasicDialog} this
30147          * @param {Number} x The new page X
30148          * @param {Number} y The new page Y
30149          */
30150         "move" : true,
30151         /**
30152          * @event resize
30153          * Fires when this dialog is resized by the user.
30154          * @param {Roo.BasicDialog} this
30155          * @param {Number} width The new width
30156          * @param {Number} height The new height
30157          */
30158         "resize" : true,
30159         /**
30160          * @event beforehide
30161          * Fires before this dialog is hidden.
30162          * @param {Roo.BasicDialog} this
30163          */
30164         "beforehide" : true,
30165         /**
30166          * @event hide
30167          * Fires when this dialog is hidden.
30168          * @param {Roo.BasicDialog} this
30169          */
30170         "hide" : true,
30171         /**
30172          * @event beforeshow
30173          * Fires before this dialog is shown.
30174          * @param {Roo.BasicDialog} this
30175          */
30176         "beforeshow" : true,
30177         /**
30178          * @event show
30179          * Fires when this dialog is shown.
30180          * @param {Roo.BasicDialog} this
30181          */
30182         "show" : true
30183     });
30184     el.on("keydown", this.onKeyDown, this);
30185     el.on("mousedown", this.toFront, this);
30186     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30187     this.el.hide();
30188     Roo.DialogManager.register(this);
30189     Roo.BasicDialog.superclass.constructor.call(this);
30190 };
30191
30192 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30193     shadowOffset: Roo.isIE ? 6 : 5,
30194     minHeight: 80,
30195     minWidth: 200,
30196     minButtonWidth: 75,
30197     defaultButton: null,
30198     buttonAlign: "right",
30199     tabTag: 'div',
30200     firstShow: true,
30201
30202     /**
30203      * Sets the dialog title text
30204      * @param {String} text The title text to display
30205      * @return {Roo.BasicDialog} this
30206      */
30207     setTitle : function(text){
30208         this.header.update(text);
30209         return this;
30210     },
30211
30212     // private
30213     closeClick : function(){
30214         this.hide();
30215     },
30216
30217     // private
30218     collapseClick : function(){
30219         this[this.collapsed ? "expand" : "collapse"]();
30220     },
30221
30222     /**
30223      * Collapses the dialog to its minimized state (only the title bar is visible).
30224      * Equivalent to the user clicking the collapse dialog button.
30225      */
30226     collapse : function(){
30227         if(!this.collapsed){
30228             this.collapsed = true;
30229             this.el.addClass("x-dlg-collapsed");
30230             this.restoreHeight = this.el.getHeight();
30231             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30232         }
30233     },
30234
30235     /**
30236      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30237      * clicking the expand dialog button.
30238      */
30239     expand : function(){
30240         if(this.collapsed){
30241             this.collapsed = false;
30242             this.el.removeClass("x-dlg-collapsed");
30243             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30244         }
30245     },
30246
30247     /**
30248      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30249      * @return {Roo.TabPanel} The tabs component
30250      */
30251     initTabs : function(){
30252         var tabs = this.getTabs();
30253         while(tabs.getTab(0)){
30254             tabs.removeTab(0);
30255         }
30256         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30257             var dom = el.dom;
30258             tabs.addTab(Roo.id(dom), dom.title);
30259             dom.title = "";
30260         });
30261         tabs.activate(0);
30262         return tabs;
30263     },
30264
30265     // private
30266     beforeResize : function(){
30267         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30268     },
30269
30270     // private
30271     onResize : function(){
30272         this.refreshSize();
30273         this.syncBodyHeight();
30274         this.adjustAssets();
30275         this.focus();
30276         this.fireEvent("resize", this, this.size.width, this.size.height);
30277     },
30278
30279     // private
30280     onKeyDown : function(e){
30281         if(this.isVisible()){
30282             this.fireEvent("keydown", this, e);
30283         }
30284     },
30285
30286     /**
30287      * Resizes the dialog.
30288      * @param {Number} width
30289      * @param {Number} height
30290      * @return {Roo.BasicDialog} this
30291      */
30292     resizeTo : function(width, height){
30293         this.el.setSize(width, height);
30294         this.size = {width: width, height: height};
30295         this.syncBodyHeight();
30296         if(this.fixedcenter){
30297             this.center();
30298         }
30299         if(this.isVisible()){
30300             this.constrainXY();
30301             this.adjustAssets();
30302         }
30303         this.fireEvent("resize", this, width, height);
30304         return this;
30305     },
30306
30307
30308     /**
30309      * Resizes the dialog to fit the specified content size.
30310      * @param {Number} width
30311      * @param {Number} height
30312      * @return {Roo.BasicDialog} this
30313      */
30314     setContentSize : function(w, h){
30315         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30316         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30317         //if(!this.el.isBorderBox()){
30318             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30319             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30320         //}
30321         if(this.tabs){
30322             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30323             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30324         }
30325         this.resizeTo(w, h);
30326         return this;
30327     },
30328
30329     /**
30330      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30331      * executed in response to a particular key being pressed while the dialog is active.
30332      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30333      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30334      * @param {Function} fn The function to call
30335      * @param {Object} scope (optional) The scope of the function
30336      * @return {Roo.BasicDialog} this
30337      */
30338     addKeyListener : function(key, fn, scope){
30339         var keyCode, shift, ctrl, alt;
30340         if(typeof key == "object" && !(key instanceof Array)){
30341             keyCode = key["key"];
30342             shift = key["shift"];
30343             ctrl = key["ctrl"];
30344             alt = key["alt"];
30345         }else{
30346             keyCode = key;
30347         }
30348         var handler = function(dlg, e){
30349             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30350                 var k = e.getKey();
30351                 if(keyCode instanceof Array){
30352                     for(var i = 0, len = keyCode.length; i < len; i++){
30353                         if(keyCode[i] == k){
30354                           fn.call(scope || window, dlg, k, e);
30355                           return;
30356                         }
30357                     }
30358                 }else{
30359                     if(k == keyCode){
30360                         fn.call(scope || window, dlg, k, e);
30361                     }
30362                 }
30363             }
30364         };
30365         this.on("keydown", handler);
30366         return this;
30367     },
30368
30369     /**
30370      * Returns the TabPanel component (creates it if it doesn't exist).
30371      * Note: If you wish to simply check for the existence of tabs without creating them,
30372      * check for a null 'tabs' property.
30373      * @return {Roo.TabPanel} The tabs component
30374      */
30375     getTabs : function(){
30376         if(!this.tabs){
30377             this.el.addClass("x-dlg-auto-tabs");
30378             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30379             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30380         }
30381         return this.tabs;
30382     },
30383
30384     /**
30385      * Adds a button to the footer section of the dialog.
30386      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30387      * object or a valid Roo.DomHelper element config
30388      * @param {Function} handler The function called when the button is clicked
30389      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30390      * @return {Roo.Button} The new button
30391      */
30392     addButton : function(config, handler, scope){
30393         var dh = Roo.DomHelper;
30394         if(!this.footer){
30395             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30396         }
30397         if(!this.btnContainer){
30398             var tb = this.footer.createChild({
30399
30400                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30401                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30402             }, null, true);
30403             this.btnContainer = tb.firstChild.firstChild.firstChild;
30404         }
30405         var bconfig = {
30406             handler: handler,
30407             scope: scope,
30408             minWidth: this.minButtonWidth,
30409             hideParent:true
30410         };
30411         if(typeof config == "string"){
30412             bconfig.text = config;
30413         }else{
30414             if(config.tag){
30415                 bconfig.dhconfig = config;
30416             }else{
30417                 Roo.apply(bconfig, config);
30418             }
30419         }
30420         var fc = false;
30421         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30422             bconfig.position = Math.max(0, bconfig.position);
30423             fc = this.btnContainer.childNodes[bconfig.position];
30424         }
30425          
30426         var btn = new Roo.Button(
30427             fc ? 
30428                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30429                 : this.btnContainer.appendChild(document.createElement("td")),
30430             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30431             bconfig
30432         );
30433         this.syncBodyHeight();
30434         if(!this.buttons){
30435             /**
30436              * Array of all the buttons that have been added to this dialog via addButton
30437              * @type Array
30438              */
30439             this.buttons = [];
30440         }
30441         this.buttons.push(btn);
30442         return btn;
30443     },
30444
30445     /**
30446      * Sets the default button to be focused when the dialog is displayed.
30447      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30448      * @return {Roo.BasicDialog} this
30449      */
30450     setDefaultButton : function(btn){
30451         this.defaultButton = btn;
30452         return this;
30453     },
30454
30455     // private
30456     getHeaderFooterHeight : function(safe){
30457         var height = 0;
30458         if(this.header){
30459            height += this.header.getHeight();
30460         }
30461         if(this.footer){
30462            var fm = this.footer.getMargins();
30463             height += (this.footer.getHeight()+fm.top+fm.bottom);
30464         }
30465         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30466         height += this.centerBg.getPadding("tb");
30467         return height;
30468     },
30469
30470     // private
30471     syncBodyHeight : function()
30472     {
30473         var bd = this.body, // the text
30474             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30475             bw = this.bwrap;
30476         var height = this.size.height - this.getHeaderFooterHeight(false);
30477         bd.setHeight(height-bd.getMargins("tb"));
30478         var hh = this.header.getHeight();
30479         var h = this.size.height-hh;
30480         cb.setHeight(h);
30481         
30482         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30483         bw.setHeight(h-cb.getPadding("tb"));
30484         
30485         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30486         bd.setWidth(bw.getWidth(true));
30487         if(this.tabs){
30488             this.tabs.syncHeight();
30489             if(Roo.isIE){
30490                 this.tabs.el.repaint();
30491             }
30492         }
30493     },
30494
30495     /**
30496      * Restores the previous state of the dialog if Roo.state is configured.
30497      * @return {Roo.BasicDialog} this
30498      */
30499     restoreState : function(){
30500         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30501         if(box && box.width){
30502             this.xy = [box.x, box.y];
30503             this.resizeTo(box.width, box.height);
30504         }
30505         return this;
30506     },
30507
30508     // private
30509     beforeShow : function(){
30510         this.expand();
30511         if(this.fixedcenter){
30512             this.xy = this.el.getCenterXY(true);
30513         }
30514         if(this.modal){
30515             Roo.get(document.body).addClass("x-body-masked");
30516             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30517             this.mask.show();
30518         }
30519         this.constrainXY();
30520     },
30521
30522     // private
30523     animShow : function(){
30524         var b = Roo.get(this.animateTarget).getBox();
30525         this.proxy.setSize(b.width, b.height);
30526         this.proxy.setLocation(b.x, b.y);
30527         this.proxy.show();
30528         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30529                     true, .35, this.showEl.createDelegate(this));
30530     },
30531
30532     /**
30533      * Shows the dialog.
30534      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30535      * @return {Roo.BasicDialog} this
30536      */
30537     show : function(animateTarget){
30538         if (this.fireEvent("beforeshow", this) === false){
30539             return;
30540         }
30541         if(this.syncHeightBeforeShow){
30542             this.syncBodyHeight();
30543         }else if(this.firstShow){
30544             this.firstShow = false;
30545             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30546         }
30547         this.animateTarget = animateTarget || this.animateTarget;
30548         if(!this.el.isVisible()){
30549             this.beforeShow();
30550             if(this.animateTarget && Roo.get(this.animateTarget)){
30551                 this.animShow();
30552             }else{
30553                 this.showEl();
30554             }
30555         }
30556         return this;
30557     },
30558
30559     // private
30560     showEl : function(){
30561         this.proxy.hide();
30562         this.el.setXY(this.xy);
30563         this.el.show();
30564         this.adjustAssets(true);
30565         this.toFront();
30566         this.focus();
30567         // IE peekaboo bug - fix found by Dave Fenwick
30568         if(Roo.isIE){
30569             this.el.repaint();
30570         }
30571         this.fireEvent("show", this);
30572     },
30573
30574     /**
30575      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30576      * dialog itself will receive focus.
30577      */
30578     focus : function(){
30579         if(this.defaultButton){
30580             this.defaultButton.focus();
30581         }else{
30582             this.focusEl.focus();
30583         }
30584     },
30585
30586     // private
30587     constrainXY : function(){
30588         if(this.constraintoviewport !== false){
30589             if(!this.viewSize){
30590                 if(this.container){
30591                     var s = this.container.getSize();
30592                     this.viewSize = [s.width, s.height];
30593                 }else{
30594                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30595                 }
30596             }
30597             var s = Roo.get(this.container||document).getScroll();
30598
30599             var x = this.xy[0], y = this.xy[1];
30600             var w = this.size.width, h = this.size.height;
30601             var vw = this.viewSize[0], vh = this.viewSize[1];
30602             // only move it if it needs it
30603             var moved = false;
30604             // first validate right/bottom
30605             if(x + w > vw+s.left){
30606                 x = vw - w;
30607                 moved = true;
30608             }
30609             if(y + h > vh+s.top){
30610                 y = vh - h;
30611                 moved = true;
30612             }
30613             // then make sure top/left isn't negative
30614             if(x < s.left){
30615                 x = s.left;
30616                 moved = true;
30617             }
30618             if(y < s.top){
30619                 y = s.top;
30620                 moved = true;
30621             }
30622             if(moved){
30623                 // cache xy
30624                 this.xy = [x, y];
30625                 if(this.isVisible()){
30626                     this.el.setLocation(x, y);
30627                     this.adjustAssets();
30628                 }
30629             }
30630         }
30631     },
30632
30633     // private
30634     onDrag : function(){
30635         if(!this.proxyDrag){
30636             this.xy = this.el.getXY();
30637             this.adjustAssets();
30638         }
30639     },
30640
30641     // private
30642     adjustAssets : function(doShow){
30643         var x = this.xy[0], y = this.xy[1];
30644         var w = this.size.width, h = this.size.height;
30645         if(doShow === true){
30646             if(this.shadow){
30647                 this.shadow.show(this.el);
30648             }
30649             if(this.shim){
30650                 this.shim.show();
30651             }
30652         }
30653         if(this.shadow && this.shadow.isVisible()){
30654             this.shadow.show(this.el);
30655         }
30656         if(this.shim && this.shim.isVisible()){
30657             this.shim.setBounds(x, y, w, h);
30658         }
30659     },
30660
30661     // private
30662     adjustViewport : function(w, h){
30663         if(!w || !h){
30664             w = Roo.lib.Dom.getViewWidth();
30665             h = Roo.lib.Dom.getViewHeight();
30666         }
30667         // cache the size
30668         this.viewSize = [w, h];
30669         if(this.modal && this.mask.isVisible()){
30670             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30671             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30672         }
30673         if(this.isVisible()){
30674             this.constrainXY();
30675         }
30676     },
30677
30678     /**
30679      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30680      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30681      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30682      */
30683     destroy : function(removeEl){
30684         if(this.isVisible()){
30685             this.animateTarget = null;
30686             this.hide();
30687         }
30688         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30689         if(this.tabs){
30690             this.tabs.destroy(removeEl);
30691         }
30692         Roo.destroy(
30693              this.shim,
30694              this.proxy,
30695              this.resizer,
30696              this.close,
30697              this.mask
30698         );
30699         if(this.dd){
30700             this.dd.unreg();
30701         }
30702         if(this.buttons){
30703            for(var i = 0, len = this.buttons.length; i < len; i++){
30704                this.buttons[i].destroy();
30705            }
30706         }
30707         this.el.removeAllListeners();
30708         if(removeEl === true){
30709             this.el.update("");
30710             this.el.remove();
30711         }
30712         Roo.DialogManager.unregister(this);
30713     },
30714
30715     // private
30716     startMove : function(){
30717         if(this.proxyDrag){
30718             this.proxy.show();
30719         }
30720         if(this.constraintoviewport !== false){
30721             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30722         }
30723     },
30724
30725     // private
30726     endMove : function(){
30727         if(!this.proxyDrag){
30728             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30729         }else{
30730             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30731             this.proxy.hide();
30732         }
30733         this.refreshSize();
30734         this.adjustAssets();
30735         this.focus();
30736         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30737     },
30738
30739     /**
30740      * Brings this dialog to the front of any other visible dialogs
30741      * @return {Roo.BasicDialog} this
30742      */
30743     toFront : function(){
30744         Roo.DialogManager.bringToFront(this);
30745         return this;
30746     },
30747
30748     /**
30749      * Sends this dialog to the back (under) of any other visible dialogs
30750      * @return {Roo.BasicDialog} this
30751      */
30752     toBack : function(){
30753         Roo.DialogManager.sendToBack(this);
30754         return this;
30755     },
30756
30757     /**
30758      * Centers this dialog in the viewport
30759      * @return {Roo.BasicDialog} this
30760      */
30761     center : function(){
30762         var xy = this.el.getCenterXY(true);
30763         this.moveTo(xy[0], xy[1]);
30764         return this;
30765     },
30766
30767     /**
30768      * Moves the dialog's top-left corner to the specified point
30769      * @param {Number} x
30770      * @param {Number} y
30771      * @return {Roo.BasicDialog} this
30772      */
30773     moveTo : function(x, y){
30774         this.xy = [x,y];
30775         if(this.isVisible()){
30776             this.el.setXY(this.xy);
30777             this.adjustAssets();
30778         }
30779         return this;
30780     },
30781
30782     /**
30783      * Aligns the dialog to the specified element
30784      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30785      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30786      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30787      * @return {Roo.BasicDialog} this
30788      */
30789     alignTo : function(element, position, offsets){
30790         this.xy = this.el.getAlignToXY(element, position, offsets);
30791         if(this.isVisible()){
30792             this.el.setXY(this.xy);
30793             this.adjustAssets();
30794         }
30795         return this;
30796     },
30797
30798     /**
30799      * Anchors an element to another element and realigns it when the window is resized.
30800      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30801      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30802      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30803      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30804      * is a number, it is used as the buffer delay (defaults to 50ms).
30805      * @return {Roo.BasicDialog} this
30806      */
30807     anchorTo : function(el, alignment, offsets, monitorScroll){
30808         var action = function(){
30809             this.alignTo(el, alignment, offsets);
30810         };
30811         Roo.EventManager.onWindowResize(action, this);
30812         var tm = typeof monitorScroll;
30813         if(tm != 'undefined'){
30814             Roo.EventManager.on(window, 'scroll', action, this,
30815                 {buffer: tm == 'number' ? monitorScroll : 50});
30816         }
30817         action.call(this);
30818         return this;
30819     },
30820
30821     /**
30822      * Returns true if the dialog is visible
30823      * @return {Boolean}
30824      */
30825     isVisible : function(){
30826         return this.el.isVisible();
30827     },
30828
30829     // private
30830     animHide : function(callback){
30831         var b = Roo.get(this.animateTarget).getBox();
30832         this.proxy.show();
30833         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30834         this.el.hide();
30835         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30836                     this.hideEl.createDelegate(this, [callback]));
30837     },
30838
30839     /**
30840      * Hides the dialog.
30841      * @param {Function} callback (optional) Function to call when the dialog is hidden
30842      * @return {Roo.BasicDialog} this
30843      */
30844     hide : function(callback){
30845         if (this.fireEvent("beforehide", this) === false){
30846             return;
30847         }
30848         if(this.shadow){
30849             this.shadow.hide();
30850         }
30851         if(this.shim) {
30852           this.shim.hide();
30853         }
30854         // sometimes animateTarget seems to get set.. causing problems...
30855         // this just double checks..
30856         if(this.animateTarget && Roo.get(this.animateTarget)) {
30857            this.animHide(callback);
30858         }else{
30859             this.el.hide();
30860             this.hideEl(callback);
30861         }
30862         return this;
30863     },
30864
30865     // private
30866     hideEl : function(callback){
30867         this.proxy.hide();
30868         if(this.modal){
30869             this.mask.hide();
30870             Roo.get(document.body).removeClass("x-body-masked");
30871         }
30872         this.fireEvent("hide", this);
30873         if(typeof callback == "function"){
30874             callback();
30875         }
30876     },
30877
30878     // private
30879     hideAction : function(){
30880         this.setLeft("-10000px");
30881         this.setTop("-10000px");
30882         this.setStyle("visibility", "hidden");
30883     },
30884
30885     // private
30886     refreshSize : function(){
30887         this.size = this.el.getSize();
30888         this.xy = this.el.getXY();
30889         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30890     },
30891
30892     // private
30893     // z-index is managed by the DialogManager and may be overwritten at any time
30894     setZIndex : function(index){
30895         if(this.modal){
30896             this.mask.setStyle("z-index", index);
30897         }
30898         if(this.shim){
30899             this.shim.setStyle("z-index", ++index);
30900         }
30901         if(this.shadow){
30902             this.shadow.setZIndex(++index);
30903         }
30904         this.el.setStyle("z-index", ++index);
30905         if(this.proxy){
30906             this.proxy.setStyle("z-index", ++index);
30907         }
30908         if(this.resizer){
30909             this.resizer.proxy.setStyle("z-index", ++index);
30910         }
30911
30912         this.lastZIndex = index;
30913     },
30914
30915     /**
30916      * Returns the element for this dialog
30917      * @return {Roo.Element} The underlying dialog Element
30918      */
30919     getEl : function(){
30920         return this.el;
30921     }
30922 });
30923
30924 /**
30925  * @class Roo.DialogManager
30926  * Provides global access to BasicDialogs that have been created and
30927  * support for z-indexing (layering) multiple open dialogs.
30928  */
30929 Roo.DialogManager = function(){
30930     var list = {};
30931     var accessList = [];
30932     var front = null;
30933
30934     // private
30935     var sortDialogs = function(d1, d2){
30936         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30937     };
30938
30939     // private
30940     var orderDialogs = function(){
30941         accessList.sort(sortDialogs);
30942         var seed = Roo.DialogManager.zseed;
30943         for(var i = 0, len = accessList.length; i < len; i++){
30944             var dlg = accessList[i];
30945             if(dlg){
30946                 dlg.setZIndex(seed + (i*10));
30947             }
30948         }
30949     };
30950
30951     return {
30952         /**
30953          * The starting z-index for BasicDialogs (defaults to 9000)
30954          * @type Number The z-index value
30955          */
30956         zseed : 9000,
30957
30958         // private
30959         register : function(dlg){
30960             list[dlg.id] = dlg;
30961             accessList.push(dlg);
30962         },
30963
30964         // private
30965         unregister : function(dlg){
30966             delete list[dlg.id];
30967             var i=0;
30968             var len=0;
30969             if(!accessList.indexOf){
30970                 for(  i = 0, len = accessList.length; i < len; i++){
30971                     if(accessList[i] == dlg){
30972                         accessList.splice(i, 1);
30973                         return;
30974                     }
30975                 }
30976             }else{
30977                  i = accessList.indexOf(dlg);
30978                 if(i != -1){
30979                     accessList.splice(i, 1);
30980                 }
30981             }
30982         },
30983
30984         /**
30985          * Gets a registered dialog by id
30986          * @param {String/Object} id The id of the dialog or a dialog
30987          * @return {Roo.BasicDialog} this
30988          */
30989         get : function(id){
30990             return typeof id == "object" ? id : list[id];
30991         },
30992
30993         /**
30994          * Brings the specified dialog to the front
30995          * @param {String/Object} dlg The id of the dialog or a dialog
30996          * @return {Roo.BasicDialog} this
30997          */
30998         bringToFront : function(dlg){
30999             dlg = this.get(dlg);
31000             if(dlg != front){
31001                 front = dlg;
31002                 dlg._lastAccess = new Date().getTime();
31003                 orderDialogs();
31004             }
31005             return dlg;
31006         },
31007
31008         /**
31009          * Sends the specified dialog to the back
31010          * @param {String/Object} dlg The id of the dialog or a dialog
31011          * @return {Roo.BasicDialog} this
31012          */
31013         sendToBack : function(dlg){
31014             dlg = this.get(dlg);
31015             dlg._lastAccess = -(new Date().getTime());
31016             orderDialogs();
31017             return dlg;
31018         },
31019
31020         /**
31021          * Hides all dialogs
31022          */
31023         hideAll : function(){
31024             for(var id in list){
31025                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31026                     list[id].hide();
31027                 }
31028             }
31029         }
31030     };
31031 }();
31032
31033 /**
31034  * @class Roo.LayoutDialog
31035  * @extends Roo.BasicDialog
31036  * Dialog which provides adjustments for working with a layout in a Dialog.
31037  * Add your necessary layout config options to the dialog's config.<br>
31038  * Example usage (including a nested layout):
31039  * <pre><code>
31040 if(!dialog){
31041     dialog = new Roo.LayoutDialog("download-dlg", {
31042         modal: true,
31043         width:600,
31044         height:450,
31045         shadow:true,
31046         minWidth:500,
31047         minHeight:350,
31048         autoTabs:true,
31049         proxyDrag:true,
31050         // layout config merges with the dialog config
31051         center:{
31052             tabPosition: "top",
31053             alwaysShowTabs: true
31054         }
31055     });
31056     dialog.addKeyListener(27, dialog.hide, dialog);
31057     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31058     dialog.addButton("Build It!", this.getDownload, this);
31059
31060     // we can even add nested layouts
31061     var innerLayout = new Roo.BorderLayout("dl-inner", {
31062         east: {
31063             initialSize: 200,
31064             autoScroll:true,
31065             split:true
31066         },
31067         center: {
31068             autoScroll:true
31069         }
31070     });
31071     innerLayout.beginUpdate();
31072     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31073     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31074     innerLayout.endUpdate(true);
31075
31076     var layout = dialog.getLayout();
31077     layout.beginUpdate();
31078     layout.add("center", new Roo.ContentPanel("standard-panel",
31079                         {title: "Download the Source", fitToFrame:true}));
31080     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31081                {title: "Build your own roo.js"}));
31082     layout.getRegion("center").showPanel(sp);
31083     layout.endUpdate();
31084 }
31085 </code></pre>
31086     * @constructor
31087     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31088     * @param {Object} config configuration options
31089   */
31090 Roo.LayoutDialog = function(el, cfg){
31091     
31092     var config=  cfg;
31093     if (typeof(cfg) == 'undefined') {
31094         config = Roo.apply({}, el);
31095         // not sure why we use documentElement here.. - it should always be body.
31096         // IE7 borks horribly if we use documentElement.
31097         // webkit also does not like documentElement - it creates a body element...
31098         el = Roo.get( document.body || document.documentElement ).createChild();
31099         //config.autoCreate = true;
31100     }
31101     
31102     
31103     config.autoTabs = false;
31104     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31105     this.body.setStyle({overflow:"hidden", position:"relative"});
31106     this.layout = new Roo.BorderLayout(this.body.dom, config);
31107     this.layout.monitorWindowResize = false;
31108     this.el.addClass("x-dlg-auto-layout");
31109     // fix case when center region overwrites center function
31110     this.center = Roo.BasicDialog.prototype.center;
31111     this.on("show", this.layout.layout, this.layout, true);
31112     if (config.items) {
31113         var xitems = config.items;
31114         delete config.items;
31115         Roo.each(xitems, this.addxtype, this);
31116     }
31117     
31118     
31119 };
31120 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31121     /**
31122      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31123      * @deprecated
31124      */
31125     endUpdate : function(){
31126         this.layout.endUpdate();
31127     },
31128
31129     /**
31130      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31131      *  @deprecated
31132      */
31133     beginUpdate : function(){
31134         this.layout.beginUpdate();
31135     },
31136
31137     /**
31138      * Get the BorderLayout for this dialog
31139      * @return {Roo.BorderLayout}
31140      */
31141     getLayout : function(){
31142         return this.layout;
31143     },
31144
31145     showEl : function(){
31146         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31147         if(Roo.isIE7){
31148             this.layout.layout();
31149         }
31150     },
31151
31152     // private
31153     // Use the syncHeightBeforeShow config option to control this automatically
31154     syncBodyHeight : function(){
31155         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31156         if(this.layout){this.layout.layout();}
31157     },
31158     
31159       /**
31160      * Add an xtype element (actually adds to the layout.)
31161      * @return {Object} xdata xtype object data.
31162      */
31163     
31164     addxtype : function(c) {
31165         return this.layout.addxtype(c);
31166     }
31167 });/*
31168  * Based on:
31169  * Ext JS Library 1.1.1
31170  * Copyright(c) 2006-2007, Ext JS, LLC.
31171  *
31172  * Originally Released Under LGPL - original licence link has changed is not relivant.
31173  *
31174  * Fork - LGPL
31175  * <script type="text/javascript">
31176  */
31177  
31178 /**
31179  * @class Roo.MessageBox
31180  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31181  * Example usage:
31182  *<pre><code>
31183 // Basic alert:
31184 Roo.Msg.alert('Status', 'Changes saved successfully.');
31185
31186 // Prompt for user data:
31187 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31188     if (btn == 'ok'){
31189         // process text value...
31190     }
31191 });
31192
31193 // Show a dialog using config options:
31194 Roo.Msg.show({
31195    title:'Save Changes?',
31196    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31197    buttons: Roo.Msg.YESNOCANCEL,
31198    fn: processResult,
31199    animEl: 'elId'
31200 });
31201 </code></pre>
31202  * @singleton
31203  */
31204 Roo.MessageBox = function(){
31205     var dlg, opt, mask, waitTimer;
31206     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31207     var buttons, activeTextEl, bwidth;
31208
31209     // private
31210     var handleButton = function(button){
31211         dlg.hide();
31212         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31213     };
31214
31215     // private
31216     var handleHide = function(){
31217         if(opt && opt.cls){
31218             dlg.el.removeClass(opt.cls);
31219         }
31220         if(waitTimer){
31221             Roo.TaskMgr.stop(waitTimer);
31222             waitTimer = null;
31223         }
31224     };
31225
31226     // private
31227     var updateButtons = function(b){
31228         var width = 0;
31229         if(!b){
31230             buttons["ok"].hide();
31231             buttons["cancel"].hide();
31232             buttons["yes"].hide();
31233             buttons["no"].hide();
31234             dlg.footer.dom.style.display = 'none';
31235             return width;
31236         }
31237         dlg.footer.dom.style.display = '';
31238         for(var k in buttons){
31239             if(typeof buttons[k] != "function"){
31240                 if(b[k]){
31241                     buttons[k].show();
31242                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31243                     width += buttons[k].el.getWidth()+15;
31244                 }else{
31245                     buttons[k].hide();
31246                 }
31247             }
31248         }
31249         return width;
31250     };
31251
31252     // private
31253     var handleEsc = function(d, k, e){
31254         if(opt && opt.closable !== false){
31255             dlg.hide();
31256         }
31257         if(e){
31258             e.stopEvent();
31259         }
31260     };
31261
31262     return {
31263         /**
31264          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31265          * @return {Roo.BasicDialog} The BasicDialog element
31266          */
31267         getDialog : function(){
31268            if(!dlg){
31269                 dlg = new Roo.BasicDialog("x-msg-box", {
31270                     autoCreate : true,
31271                     shadow: true,
31272                     draggable: true,
31273                     resizable:false,
31274                     constraintoviewport:false,
31275                     fixedcenter:true,
31276                     collapsible : false,
31277                     shim:true,
31278                     modal: true,
31279                     width:400, height:100,
31280                     buttonAlign:"center",
31281                     closeClick : function(){
31282                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31283                             handleButton("no");
31284                         }else{
31285                             handleButton("cancel");
31286                         }
31287                     }
31288                 });
31289                 dlg.on("hide", handleHide);
31290                 mask = dlg.mask;
31291                 dlg.addKeyListener(27, handleEsc);
31292                 buttons = {};
31293                 var bt = this.buttonText;
31294                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31295                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31296                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31297                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31298                 bodyEl = dlg.body.createChild({
31299
31300                     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>'
31301                 });
31302                 msgEl = bodyEl.dom.firstChild;
31303                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31304                 textboxEl.enableDisplayMode();
31305                 textboxEl.addKeyListener([10,13], function(){
31306                     if(dlg.isVisible() && opt && opt.buttons){
31307                         if(opt.buttons.ok){
31308                             handleButton("ok");
31309                         }else if(opt.buttons.yes){
31310                             handleButton("yes");
31311                         }
31312                     }
31313                 });
31314                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31315                 textareaEl.enableDisplayMode();
31316                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31317                 progressEl.enableDisplayMode();
31318                 var pf = progressEl.dom.firstChild;
31319                 if (pf) {
31320                     pp = Roo.get(pf.firstChild);
31321                     pp.setHeight(pf.offsetHeight);
31322                 }
31323                 
31324             }
31325             return dlg;
31326         },
31327
31328         /**
31329          * Updates the message box body text
31330          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31331          * the XHTML-compliant non-breaking space character '&amp;#160;')
31332          * @return {Roo.MessageBox} This message box
31333          */
31334         updateText : function(text){
31335             if(!dlg.isVisible() && !opt.width){
31336                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31337             }
31338             msgEl.innerHTML = text || '&#160;';
31339       
31340             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31341             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31342             var w = Math.max(
31343                     Math.min(opt.width || cw , this.maxWidth), 
31344                     Math.max(opt.minWidth || this.minWidth, bwidth)
31345             );
31346             if(opt.prompt){
31347                 activeTextEl.setWidth(w);
31348             }
31349             if(dlg.isVisible()){
31350                 dlg.fixedcenter = false;
31351             }
31352             // to big, make it scroll. = But as usual stupid IE does not support
31353             // !important..
31354             
31355             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31356                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31357                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31358             } else {
31359                 bodyEl.dom.style.height = '';
31360                 bodyEl.dom.style.overflowY = '';
31361             }
31362             if (cw > w) {
31363                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31364             } else {
31365                 bodyEl.dom.style.overflowX = '';
31366             }
31367             
31368             dlg.setContentSize(w, bodyEl.getHeight());
31369             if(dlg.isVisible()){
31370                 dlg.fixedcenter = true;
31371             }
31372             return this;
31373         },
31374
31375         /**
31376          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31377          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31378          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31379          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31380          * @return {Roo.MessageBox} This message box
31381          */
31382         updateProgress : function(value, text){
31383             if(text){
31384                 this.updateText(text);
31385             }
31386             if (pp) { // weird bug on my firefox - for some reason this is not defined
31387                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31388             }
31389             return this;
31390         },        
31391
31392         /**
31393          * Returns true if the message box is currently displayed
31394          * @return {Boolean} True if the message box is visible, else false
31395          */
31396         isVisible : function(){
31397             return dlg && dlg.isVisible();  
31398         },
31399
31400         /**
31401          * Hides the message box if it is displayed
31402          */
31403         hide : function(){
31404             if(this.isVisible()){
31405                 dlg.hide();
31406             }  
31407         },
31408
31409         /**
31410          * Displays a new message box, or reinitializes an existing message box, based on the config options
31411          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31412          * The following config object properties are supported:
31413          * <pre>
31414 Property    Type             Description
31415 ----------  ---------------  ------------------------------------------------------------------------------------
31416 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31417                                    closes (defaults to undefined)
31418 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31419                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31420 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31421                                    progress and wait dialogs will ignore this property and always hide the
31422                                    close button as they can only be closed programmatically.
31423 cls               String           A custom CSS class to apply to the message box element
31424 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31425                                    displayed (defaults to 75)
31426 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31427                                    function will be btn (the name of the button that was clicked, if applicable,
31428                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31429                                    Progress and wait dialogs will ignore this option since they do not respond to
31430                                    user actions and can only be closed programmatically, so any required function
31431                                    should be called by the same code after it closes the dialog.
31432 icon              String           A CSS class that provides a background image to be used as an icon for
31433                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31434 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31435 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31436 modal             Boolean          False to allow user interaction with the page while the message box is
31437                                    displayed (defaults to true)
31438 msg               String           A string that will replace the existing message box body text (defaults
31439                                    to the XHTML-compliant non-breaking space character '&#160;')
31440 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31441 progress          Boolean          True to display a progress bar (defaults to false)
31442 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31443 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31444 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31445 title             String           The title text
31446 value             String           The string value to set into the active textbox element if displayed
31447 wait              Boolean          True to display a progress bar (defaults to false)
31448 width             Number           The width of the dialog in pixels
31449 </pre>
31450          *
31451          * Example usage:
31452          * <pre><code>
31453 Roo.Msg.show({
31454    title: 'Address',
31455    msg: 'Please enter your address:',
31456    width: 300,
31457    buttons: Roo.MessageBox.OKCANCEL,
31458    multiline: true,
31459    fn: saveAddress,
31460    animEl: 'addAddressBtn'
31461 });
31462 </code></pre>
31463          * @param {Object} config Configuration options
31464          * @return {Roo.MessageBox} This message box
31465          */
31466         show : function(options)
31467         {
31468             
31469             // this causes nightmares if you show one dialog after another
31470             // especially on callbacks..
31471              
31472             if(this.isVisible()){
31473                 
31474                 this.hide();
31475                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31476                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31477                 Roo.log("New Dialog Message:" +  options.msg )
31478                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31479                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31480                 
31481             }
31482             var d = this.getDialog();
31483             opt = options;
31484             d.setTitle(opt.title || "&#160;");
31485             d.close.setDisplayed(opt.closable !== false);
31486             activeTextEl = textboxEl;
31487             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31488             if(opt.prompt){
31489                 if(opt.multiline){
31490                     textboxEl.hide();
31491                     textareaEl.show();
31492                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31493                         opt.multiline : this.defaultTextHeight);
31494                     activeTextEl = textareaEl;
31495                 }else{
31496                     textboxEl.show();
31497                     textareaEl.hide();
31498                 }
31499             }else{
31500                 textboxEl.hide();
31501                 textareaEl.hide();
31502             }
31503             progressEl.setDisplayed(opt.progress === true);
31504             this.updateProgress(0);
31505             activeTextEl.dom.value = opt.value || "";
31506             if(opt.prompt){
31507                 dlg.setDefaultButton(activeTextEl);
31508             }else{
31509                 var bs = opt.buttons;
31510                 var db = null;
31511                 if(bs && bs.ok){
31512                     db = buttons["ok"];
31513                 }else if(bs && bs.yes){
31514                     db = buttons["yes"];
31515                 }
31516                 dlg.setDefaultButton(db);
31517             }
31518             bwidth = updateButtons(opt.buttons);
31519             this.updateText(opt.msg);
31520             if(opt.cls){
31521                 d.el.addClass(opt.cls);
31522             }
31523             d.proxyDrag = opt.proxyDrag === true;
31524             d.modal = opt.modal !== false;
31525             d.mask = opt.modal !== false ? mask : false;
31526             if(!d.isVisible()){
31527                 // force it to the end of the z-index stack so it gets a cursor in FF
31528                 document.body.appendChild(dlg.el.dom);
31529                 d.animateTarget = null;
31530                 d.show(options.animEl);
31531             }
31532             return this;
31533         },
31534
31535         /**
31536          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31537          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31538          * and closing the message box when the process is complete.
31539          * @param {String} title The title bar text
31540          * @param {String} msg The message box body text
31541          * @return {Roo.MessageBox} This message box
31542          */
31543         progress : function(title, msg){
31544             this.show({
31545                 title : title,
31546                 msg : msg,
31547                 buttons: false,
31548                 progress:true,
31549                 closable:false,
31550                 minWidth: this.minProgressWidth,
31551                 modal : true
31552             });
31553             return this;
31554         },
31555
31556         /**
31557          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31558          * If a callback function is passed it will be called after the user clicks the button, and the
31559          * id of the button that was clicked will be passed as the only parameter to the callback
31560          * (could also be the top-right close button).
31561          * @param {String} title The title bar text
31562          * @param {String} msg The message box body text
31563          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31564          * @param {Object} scope (optional) The scope of the callback function
31565          * @return {Roo.MessageBox} This message box
31566          */
31567         alert : function(title, msg, fn, scope){
31568             this.show({
31569                 title : title,
31570                 msg : msg,
31571                 buttons: this.OK,
31572                 fn: fn,
31573                 scope : scope,
31574                 modal : true
31575             });
31576             return this;
31577         },
31578
31579         /**
31580          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31581          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31582          * You are responsible for closing the message box when the process is complete.
31583          * @param {String} msg The message box body text
31584          * @param {String} title (optional) The title bar text
31585          * @return {Roo.MessageBox} This message box
31586          */
31587         wait : function(msg, title){
31588             this.show({
31589                 title : title,
31590                 msg : msg,
31591                 buttons: false,
31592                 closable:false,
31593                 progress:true,
31594                 modal:true,
31595                 width:300,
31596                 wait:true
31597             });
31598             waitTimer = Roo.TaskMgr.start({
31599                 run: function(i){
31600                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31601                 },
31602                 interval: 1000
31603             });
31604             return this;
31605         },
31606
31607         /**
31608          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31609          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31610          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31611          * @param {String} title The title bar text
31612          * @param {String} msg The message box body text
31613          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31614          * @param {Object} scope (optional) The scope of the callback function
31615          * @return {Roo.MessageBox} This message box
31616          */
31617         confirm : function(title, msg, fn, scope){
31618             this.show({
31619                 title : title,
31620                 msg : msg,
31621                 buttons: this.YESNO,
31622                 fn: fn,
31623                 scope : scope,
31624                 modal : true
31625             });
31626             return this;
31627         },
31628
31629         /**
31630          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31631          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31632          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31633          * (could also be the top-right close button) and the text that was entered will be passed as the two
31634          * parameters to the callback.
31635          * @param {String} title The title bar text
31636          * @param {String} msg The message box body text
31637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31638          * @param {Object} scope (optional) The scope of the callback function
31639          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31640          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31641          * @return {Roo.MessageBox} This message box
31642          */
31643         prompt : function(title, msg, fn, scope, multiline){
31644             this.show({
31645                 title : title,
31646                 msg : msg,
31647                 buttons: this.OKCANCEL,
31648                 fn: fn,
31649                 minWidth:250,
31650                 scope : scope,
31651                 prompt:true,
31652                 multiline: multiline,
31653                 modal : true
31654             });
31655             return this;
31656         },
31657
31658         /**
31659          * Button config that displays a single OK button
31660          * @type Object
31661          */
31662         OK : {ok:true},
31663         /**
31664          * Button config that displays Yes and No buttons
31665          * @type Object
31666          */
31667         YESNO : {yes:true, no:true},
31668         /**
31669          * Button config that displays OK and Cancel buttons
31670          * @type Object
31671          */
31672         OKCANCEL : {ok:true, cancel:true},
31673         /**
31674          * Button config that displays Yes, No and Cancel buttons
31675          * @type Object
31676          */
31677         YESNOCANCEL : {yes:true, no:true, cancel:true},
31678
31679         /**
31680          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31681          * @type Number
31682          */
31683         defaultTextHeight : 75,
31684         /**
31685          * The maximum width in pixels of the message box (defaults to 600)
31686          * @type Number
31687          */
31688         maxWidth : 600,
31689         /**
31690          * The minimum width in pixels of the message box (defaults to 100)
31691          * @type Number
31692          */
31693         minWidth : 100,
31694         /**
31695          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31696          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31697          * @type Number
31698          */
31699         minProgressWidth : 250,
31700         /**
31701          * An object containing the default button text strings that can be overriden for localized language support.
31702          * Supported properties are: ok, cancel, yes and no.
31703          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31704          * @type Object
31705          */
31706         buttonText : {
31707             ok : "OK",
31708             cancel : "Cancel",
31709             yes : "Yes",
31710             no : "No"
31711         }
31712     };
31713 }();
31714
31715 /**
31716  * Shorthand for {@link Roo.MessageBox}
31717  */
31718 Roo.Msg = Roo.MessageBox;/*
31719  * Based on:
31720  * Ext JS Library 1.1.1
31721  * Copyright(c) 2006-2007, Ext JS, LLC.
31722  *
31723  * Originally Released Under LGPL - original licence link has changed is not relivant.
31724  *
31725  * Fork - LGPL
31726  * <script type="text/javascript">
31727  */
31728 /**
31729  * @class Roo.QuickTips
31730  * Provides attractive and customizable tooltips for any element.
31731  * @singleton
31732  */
31733 Roo.QuickTips = function(){
31734     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31735     var ce, bd, xy, dd;
31736     var visible = false, disabled = true, inited = false;
31737     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31738     
31739     var onOver = function(e){
31740         if(disabled){
31741             return;
31742         }
31743         var t = e.getTarget();
31744         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31745             return;
31746         }
31747         if(ce && t == ce.el){
31748             clearTimeout(hideProc);
31749             return;
31750         }
31751         if(t && tagEls[t.id]){
31752             tagEls[t.id].el = t;
31753             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31754             return;
31755         }
31756         var ttp, et = Roo.fly(t);
31757         var ns = cfg.namespace;
31758         if(tm.interceptTitles && t.title){
31759             ttp = t.title;
31760             t.qtip = ttp;
31761             t.removeAttribute("title");
31762             e.preventDefault();
31763         }else{
31764             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31765         }
31766         if(ttp){
31767             showProc = show.defer(tm.showDelay, tm, [{
31768                 el: t, 
31769                 text: ttp, 
31770                 width: et.getAttributeNS(ns, cfg.width),
31771                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31772                 title: et.getAttributeNS(ns, cfg.title),
31773                     cls: et.getAttributeNS(ns, cfg.cls)
31774             }]);
31775         }
31776     };
31777     
31778     var onOut = function(e){
31779         clearTimeout(showProc);
31780         var t = e.getTarget();
31781         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31782             hideProc = setTimeout(hide, tm.hideDelay);
31783         }
31784     };
31785     
31786     var onMove = function(e){
31787         if(disabled){
31788             return;
31789         }
31790         xy = e.getXY();
31791         xy[1] += 18;
31792         if(tm.trackMouse && ce){
31793             el.setXY(xy);
31794         }
31795     };
31796     
31797     var onDown = function(e){
31798         clearTimeout(showProc);
31799         clearTimeout(hideProc);
31800         if(!e.within(el)){
31801             if(tm.hideOnClick){
31802                 hide();
31803                 tm.disable();
31804                 tm.enable.defer(100, tm);
31805             }
31806         }
31807     };
31808     
31809     var getPad = function(){
31810         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31811     };
31812
31813     var show = function(o){
31814         if(disabled){
31815             return;
31816         }
31817         clearTimeout(dismissProc);
31818         ce = o;
31819         if(removeCls){ // in case manually hidden
31820             el.removeClass(removeCls);
31821             removeCls = null;
31822         }
31823         if(ce.cls){
31824             el.addClass(ce.cls);
31825             removeCls = ce.cls;
31826         }
31827         if(ce.title){
31828             tipTitle.update(ce.title);
31829             tipTitle.show();
31830         }else{
31831             tipTitle.update('');
31832             tipTitle.hide();
31833         }
31834         el.dom.style.width  = tm.maxWidth+'px';
31835         //tipBody.dom.style.width = '';
31836         tipBodyText.update(o.text);
31837         var p = getPad(), w = ce.width;
31838         if(!w){
31839             var td = tipBodyText.dom;
31840             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31841             if(aw > tm.maxWidth){
31842                 w = tm.maxWidth;
31843             }else if(aw < tm.minWidth){
31844                 w = tm.minWidth;
31845             }else{
31846                 w = aw;
31847             }
31848         }
31849         //tipBody.setWidth(w);
31850         el.setWidth(parseInt(w, 10) + p);
31851         if(ce.autoHide === false){
31852             close.setDisplayed(true);
31853             if(dd){
31854                 dd.unlock();
31855             }
31856         }else{
31857             close.setDisplayed(false);
31858             if(dd){
31859                 dd.lock();
31860             }
31861         }
31862         if(xy){
31863             el.avoidY = xy[1]-18;
31864             el.setXY(xy);
31865         }
31866         if(tm.animate){
31867             el.setOpacity(.1);
31868             el.setStyle("visibility", "visible");
31869             el.fadeIn({callback: afterShow});
31870         }else{
31871             afterShow();
31872         }
31873     };
31874     
31875     var afterShow = function(){
31876         if(ce){
31877             el.show();
31878             esc.enable();
31879             if(tm.autoDismiss && ce.autoHide !== false){
31880                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31881             }
31882         }
31883     };
31884     
31885     var hide = function(noanim){
31886         clearTimeout(dismissProc);
31887         clearTimeout(hideProc);
31888         ce = null;
31889         if(el.isVisible()){
31890             esc.disable();
31891             if(noanim !== true && tm.animate){
31892                 el.fadeOut({callback: afterHide});
31893             }else{
31894                 afterHide();
31895             } 
31896         }
31897     };
31898     
31899     var afterHide = function(){
31900         el.hide();
31901         if(removeCls){
31902             el.removeClass(removeCls);
31903             removeCls = null;
31904         }
31905     };
31906     
31907     return {
31908         /**
31909         * @cfg {Number} minWidth
31910         * The minimum width of the quick tip (defaults to 40)
31911         */
31912        minWidth : 40,
31913         /**
31914         * @cfg {Number} maxWidth
31915         * The maximum width of the quick tip (defaults to 300)
31916         */
31917        maxWidth : 300,
31918         /**
31919         * @cfg {Boolean} interceptTitles
31920         * True to automatically use the element's DOM title value if available (defaults to false)
31921         */
31922        interceptTitles : false,
31923         /**
31924         * @cfg {Boolean} trackMouse
31925         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31926         */
31927        trackMouse : false,
31928         /**
31929         * @cfg {Boolean} hideOnClick
31930         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31931         */
31932        hideOnClick : true,
31933         /**
31934         * @cfg {Number} showDelay
31935         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31936         */
31937        showDelay : 500,
31938         /**
31939         * @cfg {Number} hideDelay
31940         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31941         */
31942        hideDelay : 200,
31943         /**
31944         * @cfg {Boolean} autoHide
31945         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31946         * Used in conjunction with hideDelay.
31947         */
31948        autoHide : true,
31949         /**
31950         * @cfg {Boolean}
31951         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31952         * (defaults to true).  Used in conjunction with autoDismissDelay.
31953         */
31954        autoDismiss : true,
31955         /**
31956         * @cfg {Number}
31957         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31958         */
31959        autoDismissDelay : 5000,
31960        /**
31961         * @cfg {Boolean} animate
31962         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31963         */
31964        animate : false,
31965
31966        /**
31967         * @cfg {String} title
31968         * Title text to display (defaults to '').  This can be any valid HTML markup.
31969         */
31970         title: '',
31971        /**
31972         * @cfg {String} text
31973         * Body text to display (defaults to '').  This can be any valid HTML markup.
31974         */
31975         text : '',
31976        /**
31977         * @cfg {String} cls
31978         * A CSS class to apply to the base quick tip element (defaults to '').
31979         */
31980         cls : '',
31981        /**
31982         * @cfg {Number} width
31983         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31984         * minWidth or maxWidth.
31985         */
31986         width : null,
31987
31988     /**
31989      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31990      * or display QuickTips in a page.
31991      */
31992        init : function(){
31993           tm = Roo.QuickTips;
31994           cfg = tm.tagConfig;
31995           if(!inited){
31996               if(!Roo.isReady){ // allow calling of init() before onReady
31997                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31998                   return;
31999               }
32000               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32001               el.fxDefaults = {stopFx: true};
32002               // maximum custom styling
32003               //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>');
32004               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>');              
32005               tipTitle = el.child('h3');
32006               tipTitle.enableDisplayMode("block");
32007               tipBody = el.child('div.x-tip-bd');
32008               tipBodyText = el.child('div.x-tip-bd-inner');
32009               //bdLeft = el.child('div.x-tip-bd-left');
32010               //bdRight = el.child('div.x-tip-bd-right');
32011               close = el.child('div.x-tip-close');
32012               close.enableDisplayMode("block");
32013               close.on("click", hide);
32014               var d = Roo.get(document);
32015               d.on("mousedown", onDown);
32016               d.on("mouseover", onOver);
32017               d.on("mouseout", onOut);
32018               d.on("mousemove", onMove);
32019               esc = d.addKeyListener(27, hide);
32020               esc.disable();
32021               if(Roo.dd.DD){
32022                   dd = el.initDD("default", null, {
32023                       onDrag : function(){
32024                           el.sync();  
32025                       }
32026                   });
32027                   dd.setHandleElId(tipTitle.id);
32028                   dd.lock();
32029               }
32030               inited = true;
32031           }
32032           this.enable(); 
32033        },
32034
32035     /**
32036      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32037      * are supported:
32038      * <pre>
32039 Property    Type                   Description
32040 ----------  ---------------------  ------------------------------------------------------------------------
32041 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32042      * </ul>
32043      * @param {Object} config The config object
32044      */
32045        register : function(config){
32046            var cs = config instanceof Array ? config : arguments;
32047            for(var i = 0, len = cs.length; i < len; i++) {
32048                var c = cs[i];
32049                var target = c.target;
32050                if(target){
32051                    if(target instanceof Array){
32052                        for(var j = 0, jlen = target.length; j < jlen; j++){
32053                            tagEls[target[j]] = c;
32054                        }
32055                    }else{
32056                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32057                    }
32058                }
32059            }
32060        },
32061
32062     /**
32063      * Removes this quick tip from its element and destroys it.
32064      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32065      */
32066        unregister : function(el){
32067            delete tagEls[Roo.id(el)];
32068        },
32069
32070     /**
32071      * Enable this quick tip.
32072      */
32073        enable : function(){
32074            if(inited && disabled){
32075                locks.pop();
32076                if(locks.length < 1){
32077                    disabled = false;
32078                }
32079            }
32080        },
32081
32082     /**
32083      * Disable this quick tip.
32084      */
32085        disable : function(){
32086           disabled = true;
32087           clearTimeout(showProc);
32088           clearTimeout(hideProc);
32089           clearTimeout(dismissProc);
32090           if(ce){
32091               hide(true);
32092           }
32093           locks.push(1);
32094        },
32095
32096     /**
32097      * Returns true if the quick tip is enabled, else false.
32098      */
32099        isEnabled : function(){
32100             return !disabled;
32101        },
32102
32103         // private
32104        tagConfig : {
32105            namespace : "ext",
32106            attribute : "qtip",
32107            width : "width",
32108            target : "target",
32109            title : "qtitle",
32110            hide : "hide",
32111            cls : "qclass"
32112        }
32113    };
32114 }();
32115
32116 // backwards compat
32117 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32118  * Based on:
32119  * Ext JS Library 1.1.1
32120  * Copyright(c) 2006-2007, Ext JS, LLC.
32121  *
32122  * Originally Released Under LGPL - original licence link has changed is not relivant.
32123  *
32124  * Fork - LGPL
32125  * <script type="text/javascript">
32126  */
32127  
32128
32129 /**
32130  * @class Roo.tree.TreePanel
32131  * @extends Roo.data.Tree
32132
32133  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32134  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32135  * @cfg {Boolean} enableDD true to enable drag and drop
32136  * @cfg {Boolean} enableDrag true to enable just drag
32137  * @cfg {Boolean} enableDrop true to enable just drop
32138  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32139  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32140  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32141  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32142  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32143  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32144  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32145  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32146  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32147  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32148  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32149  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32150  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32151  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32152  * @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>
32153  * @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>
32154  * 
32155  * @constructor
32156  * @param {String/HTMLElement/Element} el The container element
32157  * @param {Object} config
32158  */
32159 Roo.tree.TreePanel = function(el, config){
32160     var root = false;
32161     var loader = false;
32162     if (config.root) {
32163         root = config.root;
32164         delete config.root;
32165     }
32166     if (config.loader) {
32167         loader = config.loader;
32168         delete config.loader;
32169     }
32170     
32171     Roo.apply(this, config);
32172     Roo.tree.TreePanel.superclass.constructor.call(this);
32173     this.el = Roo.get(el);
32174     this.el.addClass('x-tree');
32175     //console.log(root);
32176     if (root) {
32177         this.setRootNode( Roo.factory(root, Roo.tree));
32178     }
32179     if (loader) {
32180         this.loader = Roo.factory(loader, Roo.tree);
32181     }
32182    /**
32183     * Read-only. The id of the container element becomes this TreePanel's id.
32184     */
32185     this.id = this.el.id;
32186     this.addEvents({
32187         /**
32188         * @event beforeload
32189         * Fires before a node is loaded, return false to cancel
32190         * @param {Node} node The node being loaded
32191         */
32192         "beforeload" : true,
32193         /**
32194         * @event load
32195         * Fires when a node is loaded
32196         * @param {Node} node The node that was loaded
32197         */
32198         "load" : true,
32199         /**
32200         * @event textchange
32201         * Fires when the text for a node is changed
32202         * @param {Node} node The node
32203         * @param {String} text The new text
32204         * @param {String} oldText The old text
32205         */
32206         "textchange" : true,
32207         /**
32208         * @event beforeexpand
32209         * Fires before a node is expanded, return false to cancel.
32210         * @param {Node} node The node
32211         * @param {Boolean} deep
32212         * @param {Boolean} anim
32213         */
32214         "beforeexpand" : true,
32215         /**
32216         * @event beforecollapse
32217         * Fires before a node is collapsed, return false to cancel.
32218         * @param {Node} node The node
32219         * @param {Boolean} deep
32220         * @param {Boolean} anim
32221         */
32222         "beforecollapse" : true,
32223         /**
32224         * @event expand
32225         * Fires when a node is expanded
32226         * @param {Node} node The node
32227         */
32228         "expand" : true,
32229         /**
32230         * @event disabledchange
32231         * Fires when the disabled status of a node changes
32232         * @param {Node} node The node
32233         * @param {Boolean} disabled
32234         */
32235         "disabledchange" : true,
32236         /**
32237         * @event collapse
32238         * Fires when a node is collapsed
32239         * @param {Node} node The node
32240         */
32241         "collapse" : true,
32242         /**
32243         * @event beforeclick
32244         * Fires before click processing on a node. Return false to cancel the default action.
32245         * @param {Node} node The node
32246         * @param {Roo.EventObject} e The event object
32247         */
32248         "beforeclick":true,
32249         /**
32250         * @event checkchange
32251         * Fires when a node with a checkbox's checked property changes
32252         * @param {Node} this This node
32253         * @param {Boolean} checked
32254         */
32255         "checkchange":true,
32256         /**
32257         * @event click
32258         * Fires when a node is clicked
32259         * @param {Node} node The node
32260         * @param {Roo.EventObject} e The event object
32261         */
32262         "click":true,
32263         /**
32264         * @event dblclick
32265         * Fires when a node is double clicked
32266         * @param {Node} node The node
32267         * @param {Roo.EventObject} e The event object
32268         */
32269         "dblclick":true,
32270         /**
32271         * @event contextmenu
32272         * Fires when a node is right clicked
32273         * @param {Node} node The node
32274         * @param {Roo.EventObject} e The event object
32275         */
32276         "contextmenu":true,
32277         /**
32278         * @event beforechildrenrendered
32279         * Fires right before the child nodes for a node are rendered
32280         * @param {Node} node The node
32281         */
32282         "beforechildrenrendered":true,
32283         /**
32284         * @event startdrag
32285         * Fires when a node starts being dragged
32286         * @param {Roo.tree.TreePanel} this
32287         * @param {Roo.tree.TreeNode} node
32288         * @param {event} e The raw browser event
32289         */ 
32290        "startdrag" : true,
32291        /**
32292         * @event enddrag
32293         * Fires when a drag operation is complete
32294         * @param {Roo.tree.TreePanel} this
32295         * @param {Roo.tree.TreeNode} node
32296         * @param {event} e The raw browser event
32297         */
32298        "enddrag" : true,
32299        /**
32300         * @event dragdrop
32301         * Fires when a dragged node is dropped on a valid DD target
32302         * @param {Roo.tree.TreePanel} this
32303         * @param {Roo.tree.TreeNode} node
32304         * @param {DD} dd The dd it was dropped on
32305         * @param {event} e The raw browser event
32306         */
32307        "dragdrop" : true,
32308        /**
32309         * @event beforenodedrop
32310         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32311         * passed to handlers has the following properties:<br />
32312         * <ul style="padding:5px;padding-left:16px;">
32313         * <li>tree - The TreePanel</li>
32314         * <li>target - The node being targeted for the drop</li>
32315         * <li>data - The drag data from the drag source</li>
32316         * <li>point - The point of the drop - append, above or below</li>
32317         * <li>source - The drag source</li>
32318         * <li>rawEvent - Raw mouse event</li>
32319         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32320         * to be inserted by setting them on this object.</li>
32321         * <li>cancel - Set this to true to cancel the drop.</li>
32322         * </ul>
32323         * @param {Object} dropEvent
32324         */
32325        "beforenodedrop" : true,
32326        /**
32327         * @event nodedrop
32328         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32329         * passed to handlers has the following properties:<br />
32330         * <ul style="padding:5px;padding-left:16px;">
32331         * <li>tree - The TreePanel</li>
32332         * <li>target - The node being targeted for the drop</li>
32333         * <li>data - The drag data from the drag source</li>
32334         * <li>point - The point of the drop - append, above or below</li>
32335         * <li>source - The drag source</li>
32336         * <li>rawEvent - Raw mouse event</li>
32337         * <li>dropNode - Dropped node(s).</li>
32338         * </ul>
32339         * @param {Object} dropEvent
32340         */
32341        "nodedrop" : true,
32342         /**
32343         * @event nodedragover
32344         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32345         * passed to handlers has the following properties:<br />
32346         * <ul style="padding:5px;padding-left:16px;">
32347         * <li>tree - The TreePanel</li>
32348         * <li>target - The node being targeted for the drop</li>
32349         * <li>data - The drag data from the drag source</li>
32350         * <li>point - The point of the drop - append, above or below</li>
32351         * <li>source - The drag source</li>
32352         * <li>rawEvent - Raw mouse event</li>
32353         * <li>dropNode - Drop node(s) provided by the source.</li>
32354         * <li>cancel - Set this to true to signal drop not allowed.</li>
32355         * </ul>
32356         * @param {Object} dragOverEvent
32357         */
32358        "nodedragover" : true
32359         
32360     });
32361     if(this.singleExpand){
32362        this.on("beforeexpand", this.restrictExpand, this);
32363     }
32364     if (this.editor) {
32365         this.editor.tree = this;
32366         this.editor = Roo.factory(this.editor, Roo.tree);
32367     }
32368     
32369     if (this.selModel) {
32370         this.selModel = Roo.factory(this.selModel, Roo.tree);
32371     }
32372    
32373 };
32374 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32375     rootVisible : true,
32376     animate: Roo.enableFx,
32377     lines : true,
32378     enableDD : false,
32379     hlDrop : Roo.enableFx,
32380   
32381     renderer: false,
32382     
32383     rendererTip: false,
32384     // private
32385     restrictExpand : function(node){
32386         var p = node.parentNode;
32387         if(p){
32388             if(p.expandedChild && p.expandedChild.parentNode == p){
32389                 p.expandedChild.collapse();
32390             }
32391             p.expandedChild = node;
32392         }
32393     },
32394
32395     // private override
32396     setRootNode : function(node){
32397         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32398         if(!this.rootVisible){
32399             node.ui = new Roo.tree.RootTreeNodeUI(node);
32400         }
32401         return node;
32402     },
32403
32404     /**
32405      * Returns the container element for this TreePanel
32406      */
32407     getEl : function(){
32408         return this.el;
32409     },
32410
32411     /**
32412      * Returns the default TreeLoader for this TreePanel
32413      */
32414     getLoader : function(){
32415         return this.loader;
32416     },
32417
32418     /**
32419      * Expand all nodes
32420      */
32421     expandAll : function(){
32422         this.root.expand(true);
32423     },
32424
32425     /**
32426      * Collapse all nodes
32427      */
32428     collapseAll : function(){
32429         this.root.collapse(true);
32430     },
32431
32432     /**
32433      * Returns the selection model used by this TreePanel
32434      */
32435     getSelectionModel : function(){
32436         if(!this.selModel){
32437             this.selModel = new Roo.tree.DefaultSelectionModel();
32438         }
32439         return this.selModel;
32440     },
32441
32442     /**
32443      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32444      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32445      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32446      * @return {Array}
32447      */
32448     getChecked : function(a, startNode){
32449         startNode = startNode || this.root;
32450         var r = [];
32451         var f = function(){
32452             if(this.attributes.checked){
32453                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32454             }
32455         }
32456         startNode.cascade(f);
32457         return r;
32458     },
32459
32460     /**
32461      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32462      * @param {String} path
32463      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32464      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32465      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32466      */
32467     expandPath : function(path, attr, callback){
32468         attr = attr || "id";
32469         var keys = path.split(this.pathSeparator);
32470         var curNode = this.root;
32471         if(curNode.attributes[attr] != keys[1]){ // invalid root
32472             if(callback){
32473                 callback(false, null);
32474             }
32475             return;
32476         }
32477         var index = 1;
32478         var f = function(){
32479             if(++index == keys.length){
32480                 if(callback){
32481                     callback(true, curNode);
32482                 }
32483                 return;
32484             }
32485             var c = curNode.findChild(attr, keys[index]);
32486             if(!c){
32487                 if(callback){
32488                     callback(false, curNode);
32489                 }
32490                 return;
32491             }
32492             curNode = c;
32493             c.expand(false, false, f);
32494         };
32495         curNode.expand(false, false, f);
32496     },
32497
32498     /**
32499      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32500      * @param {String} path
32501      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32502      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32503      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32504      */
32505     selectPath : function(path, attr, callback){
32506         attr = attr || "id";
32507         var keys = path.split(this.pathSeparator);
32508         var v = keys.pop();
32509         if(keys.length > 0){
32510             var f = function(success, node){
32511                 if(success && node){
32512                     var n = node.findChild(attr, v);
32513                     if(n){
32514                         n.select();
32515                         if(callback){
32516                             callback(true, n);
32517                         }
32518                     }else if(callback){
32519                         callback(false, n);
32520                     }
32521                 }else{
32522                     if(callback){
32523                         callback(false, n);
32524                     }
32525                 }
32526             };
32527             this.expandPath(keys.join(this.pathSeparator), attr, f);
32528         }else{
32529             this.root.select();
32530             if(callback){
32531                 callback(true, this.root);
32532             }
32533         }
32534     },
32535
32536     getTreeEl : function(){
32537         return this.el;
32538     },
32539
32540     /**
32541      * Trigger rendering of this TreePanel
32542      */
32543     render : function(){
32544         if (this.innerCt) {
32545             return this; // stop it rendering more than once!!
32546         }
32547         
32548         this.innerCt = this.el.createChild({tag:"ul",
32549                cls:"x-tree-root-ct " +
32550                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32551
32552         if(this.containerScroll){
32553             Roo.dd.ScrollManager.register(this.el);
32554         }
32555         if((this.enableDD || this.enableDrop) && !this.dropZone){
32556            /**
32557             * The dropZone used by this tree if drop is enabled
32558             * @type Roo.tree.TreeDropZone
32559             */
32560              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32561                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32562            });
32563         }
32564         if((this.enableDD || this.enableDrag) && !this.dragZone){
32565            /**
32566             * The dragZone used by this tree if drag is enabled
32567             * @type Roo.tree.TreeDragZone
32568             */
32569             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32570                ddGroup: this.ddGroup || "TreeDD",
32571                scroll: this.ddScroll
32572            });
32573         }
32574         this.getSelectionModel().init(this);
32575         if (!this.root) {
32576             Roo.log("ROOT not set in tree");
32577             return this;
32578         }
32579         this.root.render();
32580         if(!this.rootVisible){
32581             this.root.renderChildren();
32582         }
32583         return this;
32584     }
32585 });/*
32586  * Based on:
32587  * Ext JS Library 1.1.1
32588  * Copyright(c) 2006-2007, Ext JS, LLC.
32589  *
32590  * Originally Released Under LGPL - original licence link has changed is not relivant.
32591  *
32592  * Fork - LGPL
32593  * <script type="text/javascript">
32594  */
32595  
32596
32597 /**
32598  * @class Roo.tree.DefaultSelectionModel
32599  * @extends Roo.util.Observable
32600  * The default single selection for a TreePanel.
32601  * @param {Object} cfg Configuration
32602  */
32603 Roo.tree.DefaultSelectionModel = function(cfg){
32604    this.selNode = null;
32605    
32606    
32607    
32608    this.addEvents({
32609        /**
32610         * @event selectionchange
32611         * Fires when the selected node changes
32612         * @param {DefaultSelectionModel} this
32613         * @param {TreeNode} node the new selection
32614         */
32615        "selectionchange" : true,
32616
32617        /**
32618         * @event beforeselect
32619         * Fires before the selected node changes, return false to cancel the change
32620         * @param {DefaultSelectionModel} this
32621         * @param {TreeNode} node the new selection
32622         * @param {TreeNode} node the old selection
32623         */
32624        "beforeselect" : true
32625    });
32626    
32627     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32628 };
32629
32630 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32631     init : function(tree){
32632         this.tree = tree;
32633         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32634         tree.on("click", this.onNodeClick, this);
32635     },
32636     
32637     onNodeClick : function(node, e){
32638         if (e.ctrlKey && this.selNode == node)  {
32639             this.unselect(node);
32640             return;
32641         }
32642         this.select(node);
32643     },
32644     
32645     /**
32646      * Select a node.
32647      * @param {TreeNode} node The node to select
32648      * @return {TreeNode} The selected node
32649      */
32650     select : function(node){
32651         var last = this.selNode;
32652         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32653             if(last){
32654                 last.ui.onSelectedChange(false);
32655             }
32656             this.selNode = node;
32657             node.ui.onSelectedChange(true);
32658             this.fireEvent("selectionchange", this, node, last);
32659         }
32660         return node;
32661     },
32662     
32663     /**
32664      * Deselect a node.
32665      * @param {TreeNode} node The node to unselect
32666      */
32667     unselect : function(node){
32668         if(this.selNode == node){
32669             this.clearSelections();
32670         }    
32671     },
32672     
32673     /**
32674      * Clear all selections
32675      */
32676     clearSelections : function(){
32677         var n = this.selNode;
32678         if(n){
32679             n.ui.onSelectedChange(false);
32680             this.selNode = null;
32681             this.fireEvent("selectionchange", this, null);
32682         }
32683         return n;
32684     },
32685     
32686     /**
32687      * Get the selected node
32688      * @return {TreeNode} The selected node
32689      */
32690     getSelectedNode : function(){
32691         return this.selNode;    
32692     },
32693     
32694     /**
32695      * Returns true if the node is selected
32696      * @param {TreeNode} node The node to check
32697      * @return {Boolean}
32698      */
32699     isSelected : function(node){
32700         return this.selNode == node;  
32701     },
32702
32703     /**
32704      * Selects the node above the selected node in the tree, intelligently walking the nodes
32705      * @return TreeNode The new selection
32706      */
32707     selectPrevious : function(){
32708         var s = this.selNode || this.lastSelNode;
32709         if(!s){
32710             return null;
32711         }
32712         var ps = s.previousSibling;
32713         if(ps){
32714             if(!ps.isExpanded() || ps.childNodes.length < 1){
32715                 return this.select(ps);
32716             } else{
32717                 var lc = ps.lastChild;
32718                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32719                     lc = lc.lastChild;
32720                 }
32721                 return this.select(lc);
32722             }
32723         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32724             return this.select(s.parentNode);
32725         }
32726         return null;
32727     },
32728
32729     /**
32730      * Selects the node above the selected node in the tree, intelligently walking the nodes
32731      * @return TreeNode The new selection
32732      */
32733     selectNext : function(){
32734         var s = this.selNode || this.lastSelNode;
32735         if(!s){
32736             return null;
32737         }
32738         if(s.firstChild && s.isExpanded()){
32739              return this.select(s.firstChild);
32740          }else if(s.nextSibling){
32741              return this.select(s.nextSibling);
32742          }else if(s.parentNode){
32743             var newS = null;
32744             s.parentNode.bubble(function(){
32745                 if(this.nextSibling){
32746                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32747                     return false;
32748                 }
32749             });
32750             return newS;
32751          }
32752         return null;
32753     },
32754
32755     onKeyDown : function(e){
32756         var s = this.selNode || this.lastSelNode;
32757         // undesirable, but required
32758         var sm = this;
32759         if(!s){
32760             return;
32761         }
32762         var k = e.getKey();
32763         switch(k){
32764              case e.DOWN:
32765                  e.stopEvent();
32766                  this.selectNext();
32767              break;
32768              case e.UP:
32769                  e.stopEvent();
32770                  this.selectPrevious();
32771              break;
32772              case e.RIGHT:
32773                  e.preventDefault();
32774                  if(s.hasChildNodes()){
32775                      if(!s.isExpanded()){
32776                          s.expand();
32777                      }else if(s.firstChild){
32778                          this.select(s.firstChild, e);
32779                      }
32780                  }
32781              break;
32782              case e.LEFT:
32783                  e.preventDefault();
32784                  if(s.hasChildNodes() && s.isExpanded()){
32785                      s.collapse();
32786                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32787                      this.select(s.parentNode, e);
32788                  }
32789              break;
32790         };
32791     }
32792 });
32793
32794 /**
32795  * @class Roo.tree.MultiSelectionModel
32796  * @extends Roo.util.Observable
32797  * Multi selection for a TreePanel.
32798  * @param {Object} cfg Configuration
32799  */
32800 Roo.tree.MultiSelectionModel = function(){
32801    this.selNodes = [];
32802    this.selMap = {};
32803    this.addEvents({
32804        /**
32805         * @event selectionchange
32806         * Fires when the selected nodes change
32807         * @param {MultiSelectionModel} this
32808         * @param {Array} nodes Array of the selected nodes
32809         */
32810        "selectionchange" : true
32811    });
32812    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32813    
32814 };
32815
32816 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32817     init : function(tree){
32818         this.tree = tree;
32819         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32820         tree.on("click", this.onNodeClick, this);
32821     },
32822     
32823     onNodeClick : function(node, e){
32824         this.select(node, e, e.ctrlKey);
32825     },
32826     
32827     /**
32828      * Select a node.
32829      * @param {TreeNode} node The node to select
32830      * @param {EventObject} e (optional) An event associated with the selection
32831      * @param {Boolean} keepExisting True to retain existing selections
32832      * @return {TreeNode} The selected node
32833      */
32834     select : function(node, e, keepExisting){
32835         if(keepExisting !== true){
32836             this.clearSelections(true);
32837         }
32838         if(this.isSelected(node)){
32839             this.lastSelNode = node;
32840             return node;
32841         }
32842         this.selNodes.push(node);
32843         this.selMap[node.id] = node;
32844         this.lastSelNode = node;
32845         node.ui.onSelectedChange(true);
32846         this.fireEvent("selectionchange", this, this.selNodes);
32847         return node;
32848     },
32849     
32850     /**
32851      * Deselect a node.
32852      * @param {TreeNode} node The node to unselect
32853      */
32854     unselect : function(node){
32855         if(this.selMap[node.id]){
32856             node.ui.onSelectedChange(false);
32857             var sn = this.selNodes;
32858             var index = -1;
32859             if(sn.indexOf){
32860                 index = sn.indexOf(node);
32861             }else{
32862                 for(var i = 0, len = sn.length; i < len; i++){
32863                     if(sn[i] == node){
32864                         index = i;
32865                         break;
32866                     }
32867                 }
32868             }
32869             if(index != -1){
32870                 this.selNodes.splice(index, 1);
32871             }
32872             delete this.selMap[node.id];
32873             this.fireEvent("selectionchange", this, this.selNodes);
32874         }
32875     },
32876     
32877     /**
32878      * Clear all selections
32879      */
32880     clearSelections : function(suppressEvent){
32881         var sn = this.selNodes;
32882         if(sn.length > 0){
32883             for(var i = 0, len = sn.length; i < len; i++){
32884                 sn[i].ui.onSelectedChange(false);
32885             }
32886             this.selNodes = [];
32887             this.selMap = {};
32888             if(suppressEvent !== true){
32889                 this.fireEvent("selectionchange", this, this.selNodes);
32890             }
32891         }
32892     },
32893     
32894     /**
32895      * Returns true if the node is selected
32896      * @param {TreeNode} node The node to check
32897      * @return {Boolean}
32898      */
32899     isSelected : function(node){
32900         return this.selMap[node.id] ? true : false;  
32901     },
32902     
32903     /**
32904      * Returns an array of the selected nodes
32905      * @return {Array}
32906      */
32907     getSelectedNodes : function(){
32908         return this.selNodes;    
32909     },
32910
32911     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32912
32913     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32914
32915     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32916 });/*
32917  * Based on:
32918  * Ext JS Library 1.1.1
32919  * Copyright(c) 2006-2007, Ext JS, LLC.
32920  *
32921  * Originally Released Under LGPL - original licence link has changed is not relivant.
32922  *
32923  * Fork - LGPL
32924  * <script type="text/javascript">
32925  */
32926  
32927 /**
32928  * @class Roo.tree.TreeNode
32929  * @extends Roo.data.Node
32930  * @cfg {String} text The text for this node
32931  * @cfg {Boolean} expanded true to start the node expanded
32932  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32933  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32934  * @cfg {Boolean} disabled true to start the node disabled
32935  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32936  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32937  * @cfg {String} cls A css class to be added to the node
32938  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32939  * @cfg {String} href URL of the link used for the node (defaults to #)
32940  * @cfg {String} hrefTarget target frame for the link
32941  * @cfg {String} qtip An Ext QuickTip for the node
32942  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32943  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32944  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32945  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32946  * (defaults to undefined with no checkbox rendered)
32947  * @constructor
32948  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32949  */
32950 Roo.tree.TreeNode = function(attributes){
32951     attributes = attributes || {};
32952     if(typeof attributes == "string"){
32953         attributes = {text: attributes};
32954     }
32955     this.childrenRendered = false;
32956     this.rendered = false;
32957     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32958     this.expanded = attributes.expanded === true;
32959     this.isTarget = attributes.isTarget !== false;
32960     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32961     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32962
32963     /**
32964      * Read-only. The text for this node. To change it use setText().
32965      * @type String
32966      */
32967     this.text = attributes.text;
32968     /**
32969      * True if this node is disabled.
32970      * @type Boolean
32971      */
32972     this.disabled = attributes.disabled === true;
32973
32974     this.addEvents({
32975         /**
32976         * @event textchange
32977         * Fires when the text for this node is changed
32978         * @param {Node} this This node
32979         * @param {String} text The new text
32980         * @param {String} oldText The old text
32981         */
32982         "textchange" : true,
32983         /**
32984         * @event beforeexpand
32985         * Fires before this node is expanded, return false to cancel.
32986         * @param {Node} this This node
32987         * @param {Boolean} deep
32988         * @param {Boolean} anim
32989         */
32990         "beforeexpand" : true,
32991         /**
32992         * @event beforecollapse
32993         * Fires before this node is collapsed, return false to cancel.
32994         * @param {Node} this This node
32995         * @param {Boolean} deep
32996         * @param {Boolean} anim
32997         */
32998         "beforecollapse" : true,
32999         /**
33000         * @event expand
33001         * Fires when this node is expanded
33002         * @param {Node} this This node
33003         */
33004         "expand" : true,
33005         /**
33006         * @event disabledchange
33007         * Fires when the disabled status of this node changes
33008         * @param {Node} this This node
33009         * @param {Boolean} disabled
33010         */
33011         "disabledchange" : true,
33012         /**
33013         * @event collapse
33014         * Fires when this node is collapsed
33015         * @param {Node} this This node
33016         */
33017         "collapse" : true,
33018         /**
33019         * @event beforeclick
33020         * Fires before click processing. Return false to cancel the default action.
33021         * @param {Node} this This node
33022         * @param {Roo.EventObject} e The event object
33023         */
33024         "beforeclick":true,
33025         /**
33026         * @event checkchange
33027         * Fires when a node with a checkbox's checked property changes
33028         * @param {Node} this This node
33029         * @param {Boolean} checked
33030         */
33031         "checkchange":true,
33032         /**
33033         * @event click
33034         * Fires when this node is clicked
33035         * @param {Node} this This node
33036         * @param {Roo.EventObject} e The event object
33037         */
33038         "click":true,
33039         /**
33040         * @event dblclick
33041         * Fires when this node is double clicked
33042         * @param {Node} this This node
33043         * @param {Roo.EventObject} e The event object
33044         */
33045         "dblclick":true,
33046         /**
33047         * @event contextmenu
33048         * Fires when this node is right clicked
33049         * @param {Node} this This node
33050         * @param {Roo.EventObject} e The event object
33051         */
33052         "contextmenu":true,
33053         /**
33054         * @event beforechildrenrendered
33055         * Fires right before the child nodes for this node are rendered
33056         * @param {Node} this This node
33057         */
33058         "beforechildrenrendered":true
33059     });
33060
33061     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33062
33063     /**
33064      * Read-only. The UI for this node
33065      * @type TreeNodeUI
33066      */
33067     this.ui = new uiClass(this);
33068     
33069     // finally support items[]
33070     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33071         return;
33072     }
33073     
33074     
33075     Roo.each(this.attributes.items, function(c) {
33076         this.appendChild(Roo.factory(c,Roo.Tree));
33077     }, this);
33078     delete this.attributes.items;
33079     
33080     
33081     
33082 };
33083 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33084     preventHScroll: true,
33085     /**
33086      * Returns true if this node is expanded
33087      * @return {Boolean}
33088      */
33089     isExpanded : function(){
33090         return this.expanded;
33091     },
33092
33093     /**
33094      * Returns the UI object for this node
33095      * @return {TreeNodeUI}
33096      */
33097     getUI : function(){
33098         return this.ui;
33099     },
33100
33101     // private override
33102     setFirstChild : function(node){
33103         var of = this.firstChild;
33104         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33105         if(this.childrenRendered && of && node != of){
33106             of.renderIndent(true, true);
33107         }
33108         if(this.rendered){
33109             this.renderIndent(true, true);
33110         }
33111     },
33112
33113     // private override
33114     setLastChild : function(node){
33115         var ol = this.lastChild;
33116         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33117         if(this.childrenRendered && ol && node != ol){
33118             ol.renderIndent(true, true);
33119         }
33120         if(this.rendered){
33121             this.renderIndent(true, true);
33122         }
33123     },
33124
33125     // these methods are overridden to provide lazy rendering support
33126     // private override
33127     appendChild : function()
33128     {
33129         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33130         if(node && this.childrenRendered){
33131             node.render();
33132         }
33133         this.ui.updateExpandIcon();
33134         return node;
33135     },
33136
33137     // private override
33138     removeChild : function(node){
33139         this.ownerTree.getSelectionModel().unselect(node);
33140         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33141         // if it's been rendered remove dom node
33142         if(this.childrenRendered){
33143             node.ui.remove();
33144         }
33145         if(this.childNodes.length < 1){
33146             this.collapse(false, false);
33147         }else{
33148             this.ui.updateExpandIcon();
33149         }
33150         if(!this.firstChild) {
33151             this.childrenRendered = false;
33152         }
33153         return node;
33154     },
33155
33156     // private override
33157     insertBefore : function(node, refNode){
33158         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33159         if(newNode && refNode && this.childrenRendered){
33160             node.render();
33161         }
33162         this.ui.updateExpandIcon();
33163         return newNode;
33164     },
33165
33166     /**
33167      * Sets the text for this node
33168      * @param {String} text
33169      */
33170     setText : function(text){
33171         var oldText = this.text;
33172         this.text = text;
33173         this.attributes.text = text;
33174         if(this.rendered){ // event without subscribing
33175             this.ui.onTextChange(this, text, oldText);
33176         }
33177         this.fireEvent("textchange", this, text, oldText);
33178     },
33179
33180     /**
33181      * Triggers selection of this node
33182      */
33183     select : function(){
33184         this.getOwnerTree().getSelectionModel().select(this);
33185     },
33186
33187     /**
33188      * Triggers deselection of this node
33189      */
33190     unselect : function(){
33191         this.getOwnerTree().getSelectionModel().unselect(this);
33192     },
33193
33194     /**
33195      * Returns true if this node is selected
33196      * @return {Boolean}
33197      */
33198     isSelected : function(){
33199         return this.getOwnerTree().getSelectionModel().isSelected(this);
33200     },
33201
33202     /**
33203      * Expand this node.
33204      * @param {Boolean} deep (optional) True to expand all children as well
33205      * @param {Boolean} anim (optional) false to cancel the default animation
33206      * @param {Function} callback (optional) A callback to be called when
33207      * expanding this node completes (does not wait for deep expand to complete).
33208      * Called with 1 parameter, this node.
33209      */
33210     expand : function(deep, anim, callback){
33211         if(!this.expanded){
33212             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33213                 return;
33214             }
33215             if(!this.childrenRendered){
33216                 this.renderChildren();
33217             }
33218             this.expanded = true;
33219             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33220                 this.ui.animExpand(function(){
33221                     this.fireEvent("expand", this);
33222                     if(typeof callback == "function"){
33223                         callback(this);
33224                     }
33225                     if(deep === true){
33226                         this.expandChildNodes(true);
33227                     }
33228                 }.createDelegate(this));
33229                 return;
33230             }else{
33231                 this.ui.expand();
33232                 this.fireEvent("expand", this);
33233                 if(typeof callback == "function"){
33234                     callback(this);
33235                 }
33236             }
33237         }else{
33238            if(typeof callback == "function"){
33239                callback(this);
33240            }
33241         }
33242         if(deep === true){
33243             this.expandChildNodes(true);
33244         }
33245     },
33246
33247     isHiddenRoot : function(){
33248         return this.isRoot && !this.getOwnerTree().rootVisible;
33249     },
33250
33251     /**
33252      * Collapse this node.
33253      * @param {Boolean} deep (optional) True to collapse all children as well
33254      * @param {Boolean} anim (optional) false to cancel the default animation
33255      */
33256     collapse : function(deep, anim){
33257         if(this.expanded && !this.isHiddenRoot()){
33258             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33259                 return;
33260             }
33261             this.expanded = false;
33262             if((this.getOwnerTree().animate && anim !== false) || anim){
33263                 this.ui.animCollapse(function(){
33264                     this.fireEvent("collapse", this);
33265                     if(deep === true){
33266                         this.collapseChildNodes(true);
33267                     }
33268                 }.createDelegate(this));
33269                 return;
33270             }else{
33271                 this.ui.collapse();
33272                 this.fireEvent("collapse", this);
33273             }
33274         }
33275         if(deep === true){
33276             var cs = this.childNodes;
33277             for(var i = 0, len = cs.length; i < len; i++) {
33278                 cs[i].collapse(true, false);
33279             }
33280         }
33281     },
33282
33283     // private
33284     delayedExpand : function(delay){
33285         if(!this.expandProcId){
33286             this.expandProcId = this.expand.defer(delay, this);
33287         }
33288     },
33289
33290     // private
33291     cancelExpand : function(){
33292         if(this.expandProcId){
33293             clearTimeout(this.expandProcId);
33294         }
33295         this.expandProcId = false;
33296     },
33297
33298     /**
33299      * Toggles expanded/collapsed state of the node
33300      */
33301     toggle : function(){
33302         if(this.expanded){
33303             this.collapse();
33304         }else{
33305             this.expand();
33306         }
33307     },
33308
33309     /**
33310      * Ensures all parent nodes are expanded
33311      */
33312     ensureVisible : function(callback){
33313         var tree = this.getOwnerTree();
33314         tree.expandPath(this.parentNode.getPath(), false, function(){
33315             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33316             Roo.callback(callback);
33317         }.createDelegate(this));
33318     },
33319
33320     /**
33321      * Expand all child nodes
33322      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33323      */
33324     expandChildNodes : function(deep){
33325         var cs = this.childNodes;
33326         for(var i = 0, len = cs.length; i < len; i++) {
33327                 cs[i].expand(deep);
33328         }
33329     },
33330
33331     /**
33332      * Collapse all child nodes
33333      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33334      */
33335     collapseChildNodes : function(deep){
33336         var cs = this.childNodes;
33337         for(var i = 0, len = cs.length; i < len; i++) {
33338                 cs[i].collapse(deep);
33339         }
33340     },
33341
33342     /**
33343      * Disables this node
33344      */
33345     disable : function(){
33346         this.disabled = true;
33347         this.unselect();
33348         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33349             this.ui.onDisableChange(this, true);
33350         }
33351         this.fireEvent("disabledchange", this, true);
33352     },
33353
33354     /**
33355      * Enables this node
33356      */
33357     enable : function(){
33358         this.disabled = false;
33359         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33360             this.ui.onDisableChange(this, false);
33361         }
33362         this.fireEvent("disabledchange", this, false);
33363     },
33364
33365     // private
33366     renderChildren : function(suppressEvent){
33367         if(suppressEvent !== false){
33368             this.fireEvent("beforechildrenrendered", this);
33369         }
33370         var cs = this.childNodes;
33371         for(var i = 0, len = cs.length; i < len; i++){
33372             cs[i].render(true);
33373         }
33374         this.childrenRendered = true;
33375     },
33376
33377     // private
33378     sort : function(fn, scope){
33379         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33380         if(this.childrenRendered){
33381             var cs = this.childNodes;
33382             for(var i = 0, len = cs.length; i < len; i++){
33383                 cs[i].render(true);
33384             }
33385         }
33386     },
33387
33388     // private
33389     render : function(bulkRender){
33390         this.ui.render(bulkRender);
33391         if(!this.rendered){
33392             this.rendered = true;
33393             if(this.expanded){
33394                 this.expanded = false;
33395                 this.expand(false, false);
33396             }
33397         }
33398     },
33399
33400     // private
33401     renderIndent : function(deep, refresh){
33402         if(refresh){
33403             this.ui.childIndent = null;
33404         }
33405         this.ui.renderIndent();
33406         if(deep === true && this.childrenRendered){
33407             var cs = this.childNodes;
33408             for(var i = 0, len = cs.length; i < len; i++){
33409                 cs[i].renderIndent(true, refresh);
33410             }
33411         }
33412     }
33413 });/*
33414  * Based on:
33415  * Ext JS Library 1.1.1
33416  * Copyright(c) 2006-2007, Ext JS, LLC.
33417  *
33418  * Originally Released Under LGPL - original licence link has changed is not relivant.
33419  *
33420  * Fork - LGPL
33421  * <script type="text/javascript">
33422  */
33423  
33424 /**
33425  * @class Roo.tree.AsyncTreeNode
33426  * @extends Roo.tree.TreeNode
33427  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33428  * @constructor
33429  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33430  */
33431  Roo.tree.AsyncTreeNode = function(config){
33432     this.loaded = false;
33433     this.loading = false;
33434     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33435     /**
33436     * @event beforeload
33437     * Fires before this node is loaded, return false to cancel
33438     * @param {Node} this This node
33439     */
33440     this.addEvents({'beforeload':true, 'load': true});
33441     /**
33442     * @event load
33443     * Fires when this node is loaded
33444     * @param {Node} this This node
33445     */
33446     /**
33447      * The loader used by this node (defaults to using the tree's defined loader)
33448      * @type TreeLoader
33449      * @property loader
33450      */
33451 };
33452 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33453     expand : function(deep, anim, callback){
33454         if(this.loading){ // if an async load is already running, waiting til it's done
33455             var timer;
33456             var f = function(){
33457                 if(!this.loading){ // done loading
33458                     clearInterval(timer);
33459                     this.expand(deep, anim, callback);
33460                 }
33461             }.createDelegate(this);
33462             timer = setInterval(f, 200);
33463             return;
33464         }
33465         if(!this.loaded){
33466             if(this.fireEvent("beforeload", this) === false){
33467                 return;
33468             }
33469             this.loading = true;
33470             this.ui.beforeLoad(this);
33471             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33472             if(loader){
33473                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33474                 return;
33475             }
33476         }
33477         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33478     },
33479     
33480     /**
33481      * Returns true if this node is currently loading
33482      * @return {Boolean}
33483      */
33484     isLoading : function(){
33485         return this.loading;  
33486     },
33487     
33488     loadComplete : function(deep, anim, callback){
33489         this.loading = false;
33490         this.loaded = true;
33491         this.ui.afterLoad(this);
33492         this.fireEvent("load", this);
33493         this.expand(deep, anim, callback);
33494     },
33495     
33496     /**
33497      * Returns true if this node has been loaded
33498      * @return {Boolean}
33499      */
33500     isLoaded : function(){
33501         return this.loaded;
33502     },
33503     
33504     hasChildNodes : function(){
33505         if(!this.isLeaf() && !this.loaded){
33506             return true;
33507         }else{
33508             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33509         }
33510     },
33511
33512     /**
33513      * Trigger a reload for this node
33514      * @param {Function} callback
33515      */
33516     reload : function(callback){
33517         this.collapse(false, false);
33518         while(this.firstChild){
33519             this.removeChild(this.firstChild);
33520         }
33521         this.childrenRendered = false;
33522         this.loaded = false;
33523         if(this.isHiddenRoot()){
33524             this.expanded = false;
33525         }
33526         this.expand(false, false, callback);
33527     }
33528 });/*
33529  * Based on:
33530  * Ext JS Library 1.1.1
33531  * Copyright(c) 2006-2007, Ext JS, LLC.
33532  *
33533  * Originally Released Under LGPL - original licence link has changed is not relivant.
33534  *
33535  * Fork - LGPL
33536  * <script type="text/javascript">
33537  */
33538  
33539 /**
33540  * @class Roo.tree.TreeNodeUI
33541  * @constructor
33542  * @param {Object} node The node to render
33543  * The TreeNode UI implementation is separate from the
33544  * tree implementation. Unless you are customizing the tree UI,
33545  * you should never have to use this directly.
33546  */
33547 Roo.tree.TreeNodeUI = function(node){
33548     this.node = node;
33549     this.rendered = false;
33550     this.animating = false;
33551     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33552 };
33553
33554 Roo.tree.TreeNodeUI.prototype = {
33555     removeChild : function(node){
33556         if(this.rendered){
33557             this.ctNode.removeChild(node.ui.getEl());
33558         }
33559     },
33560
33561     beforeLoad : function(){
33562          this.addClass("x-tree-node-loading");
33563     },
33564
33565     afterLoad : function(){
33566          this.removeClass("x-tree-node-loading");
33567     },
33568
33569     onTextChange : function(node, text, oldText){
33570         if(this.rendered){
33571             this.textNode.innerHTML = text;
33572         }
33573     },
33574
33575     onDisableChange : function(node, state){
33576         this.disabled = state;
33577         if(state){
33578             this.addClass("x-tree-node-disabled");
33579         }else{
33580             this.removeClass("x-tree-node-disabled");
33581         }
33582     },
33583
33584     onSelectedChange : function(state){
33585         if(state){
33586             this.focus();
33587             this.addClass("x-tree-selected");
33588         }else{
33589             //this.blur();
33590             this.removeClass("x-tree-selected");
33591         }
33592     },
33593
33594     onMove : function(tree, node, oldParent, newParent, index, refNode){
33595         this.childIndent = null;
33596         if(this.rendered){
33597             var targetNode = newParent.ui.getContainer();
33598             if(!targetNode){//target not rendered
33599                 this.holder = document.createElement("div");
33600                 this.holder.appendChild(this.wrap);
33601                 return;
33602             }
33603             var insertBefore = refNode ? refNode.ui.getEl() : null;
33604             if(insertBefore){
33605                 targetNode.insertBefore(this.wrap, insertBefore);
33606             }else{
33607                 targetNode.appendChild(this.wrap);
33608             }
33609             this.node.renderIndent(true);
33610         }
33611     },
33612
33613     addClass : function(cls){
33614         if(this.elNode){
33615             Roo.fly(this.elNode).addClass(cls);
33616         }
33617     },
33618
33619     removeClass : function(cls){
33620         if(this.elNode){
33621             Roo.fly(this.elNode).removeClass(cls);
33622         }
33623     },
33624
33625     remove : function(){
33626         if(this.rendered){
33627             this.holder = document.createElement("div");
33628             this.holder.appendChild(this.wrap);
33629         }
33630     },
33631
33632     fireEvent : function(){
33633         return this.node.fireEvent.apply(this.node, arguments);
33634     },
33635
33636     initEvents : function(){
33637         this.node.on("move", this.onMove, this);
33638         var E = Roo.EventManager;
33639         var a = this.anchor;
33640
33641         var el = Roo.fly(a, '_treeui');
33642
33643         if(Roo.isOpera){ // opera render bug ignores the CSS
33644             el.setStyle("text-decoration", "none");
33645         }
33646
33647         el.on("click", this.onClick, this);
33648         el.on("dblclick", this.onDblClick, this);
33649
33650         if(this.checkbox){
33651             Roo.EventManager.on(this.checkbox,
33652                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33653         }
33654
33655         el.on("contextmenu", this.onContextMenu, this);
33656
33657         var icon = Roo.fly(this.iconNode);
33658         icon.on("click", this.onClick, this);
33659         icon.on("dblclick", this.onDblClick, this);
33660         icon.on("contextmenu", this.onContextMenu, this);
33661         E.on(this.ecNode, "click", this.ecClick, this, true);
33662
33663         if(this.node.disabled){
33664             this.addClass("x-tree-node-disabled");
33665         }
33666         if(this.node.hidden){
33667             this.addClass("x-tree-node-disabled");
33668         }
33669         var ot = this.node.getOwnerTree();
33670         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33671         if(dd && (!this.node.isRoot || ot.rootVisible)){
33672             Roo.dd.Registry.register(this.elNode, {
33673                 node: this.node,
33674                 handles: this.getDDHandles(),
33675                 isHandle: false
33676             });
33677         }
33678     },
33679
33680     getDDHandles : function(){
33681         return [this.iconNode, this.textNode];
33682     },
33683
33684     hide : function(){
33685         if(this.rendered){
33686             this.wrap.style.display = "none";
33687         }
33688     },
33689
33690     show : function(){
33691         if(this.rendered){
33692             this.wrap.style.display = "";
33693         }
33694     },
33695
33696     onContextMenu : function(e){
33697         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33698             e.preventDefault();
33699             this.focus();
33700             this.fireEvent("contextmenu", this.node, e);
33701         }
33702     },
33703
33704     onClick : function(e){
33705         if(this.dropping){
33706             e.stopEvent();
33707             return;
33708         }
33709         if(this.fireEvent("beforeclick", this.node, e) !== false){
33710             if(!this.disabled && this.node.attributes.href){
33711                 this.fireEvent("click", this.node, e);
33712                 return;
33713             }
33714             e.preventDefault();
33715             if(this.disabled){
33716                 return;
33717             }
33718
33719             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33720                 this.node.toggle();
33721             }
33722
33723             this.fireEvent("click", this.node, e);
33724         }else{
33725             e.stopEvent();
33726         }
33727     },
33728
33729     onDblClick : function(e){
33730         e.preventDefault();
33731         if(this.disabled){
33732             return;
33733         }
33734         if(this.checkbox){
33735             this.toggleCheck();
33736         }
33737         if(!this.animating && this.node.hasChildNodes()){
33738             this.node.toggle();
33739         }
33740         this.fireEvent("dblclick", this.node, e);
33741     },
33742
33743     onCheckChange : function(){
33744         var checked = this.checkbox.checked;
33745         this.node.attributes.checked = checked;
33746         this.fireEvent('checkchange', this.node, checked);
33747     },
33748
33749     ecClick : function(e){
33750         if(!this.animating && this.node.hasChildNodes()){
33751             this.node.toggle();
33752         }
33753     },
33754
33755     startDrop : function(){
33756         this.dropping = true;
33757     },
33758
33759     // delayed drop so the click event doesn't get fired on a drop
33760     endDrop : function(){
33761        setTimeout(function(){
33762            this.dropping = false;
33763        }.createDelegate(this), 50);
33764     },
33765
33766     expand : function(){
33767         this.updateExpandIcon();
33768         this.ctNode.style.display = "";
33769     },
33770
33771     focus : function(){
33772         if(!this.node.preventHScroll){
33773             try{this.anchor.focus();
33774             }catch(e){}
33775         }else if(!Roo.isIE){
33776             try{
33777                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33778                 var l = noscroll.scrollLeft;
33779                 this.anchor.focus();
33780                 noscroll.scrollLeft = l;
33781             }catch(e){}
33782         }
33783     },
33784
33785     toggleCheck : function(value){
33786         var cb = this.checkbox;
33787         if(cb){
33788             cb.checked = (value === undefined ? !cb.checked : value);
33789         }
33790     },
33791
33792     blur : function(){
33793         try{
33794             this.anchor.blur();
33795         }catch(e){}
33796     },
33797
33798     animExpand : function(callback){
33799         var ct = Roo.get(this.ctNode);
33800         ct.stopFx();
33801         if(!this.node.hasChildNodes()){
33802             this.updateExpandIcon();
33803             this.ctNode.style.display = "";
33804             Roo.callback(callback);
33805             return;
33806         }
33807         this.animating = true;
33808         this.updateExpandIcon();
33809
33810         ct.slideIn('t', {
33811            callback : function(){
33812                this.animating = false;
33813                Roo.callback(callback);
33814             },
33815             scope: this,
33816             duration: this.node.ownerTree.duration || .25
33817         });
33818     },
33819
33820     highlight : function(){
33821         var tree = this.node.getOwnerTree();
33822         Roo.fly(this.wrap).highlight(
33823             tree.hlColor || "C3DAF9",
33824             {endColor: tree.hlBaseColor}
33825         );
33826     },
33827
33828     collapse : function(){
33829         this.updateExpandIcon();
33830         this.ctNode.style.display = "none";
33831     },
33832
33833     animCollapse : function(callback){
33834         var ct = Roo.get(this.ctNode);
33835         ct.enableDisplayMode('block');
33836         ct.stopFx();
33837
33838         this.animating = true;
33839         this.updateExpandIcon();
33840
33841         ct.slideOut('t', {
33842             callback : function(){
33843                this.animating = false;
33844                Roo.callback(callback);
33845             },
33846             scope: this,
33847             duration: this.node.ownerTree.duration || .25
33848         });
33849     },
33850
33851     getContainer : function(){
33852         return this.ctNode;
33853     },
33854
33855     getEl : function(){
33856         return this.wrap;
33857     },
33858
33859     appendDDGhost : function(ghostNode){
33860         ghostNode.appendChild(this.elNode.cloneNode(true));
33861     },
33862
33863     getDDRepairXY : function(){
33864         return Roo.lib.Dom.getXY(this.iconNode);
33865     },
33866
33867     onRender : function(){
33868         this.render();
33869     },
33870
33871     render : function(bulkRender){
33872         var n = this.node, a = n.attributes;
33873         var targetNode = n.parentNode ?
33874               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33875
33876         if(!this.rendered){
33877             this.rendered = true;
33878
33879             this.renderElements(n, a, targetNode, bulkRender);
33880
33881             if(a.qtip){
33882                if(this.textNode.setAttributeNS){
33883                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33884                    if(a.qtipTitle){
33885                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33886                    }
33887                }else{
33888                    this.textNode.setAttribute("ext:qtip", a.qtip);
33889                    if(a.qtipTitle){
33890                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33891                    }
33892                }
33893             }else if(a.qtipCfg){
33894                 a.qtipCfg.target = Roo.id(this.textNode);
33895                 Roo.QuickTips.register(a.qtipCfg);
33896             }
33897             this.initEvents();
33898             if(!this.node.expanded){
33899                 this.updateExpandIcon();
33900             }
33901         }else{
33902             if(bulkRender === true) {
33903                 targetNode.appendChild(this.wrap);
33904             }
33905         }
33906     },
33907
33908     renderElements : function(n, a, targetNode, bulkRender)
33909     {
33910         // add some indent caching, this helps performance when rendering a large tree
33911         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33912         var t = n.getOwnerTree();
33913         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33914         if (typeof(n.attributes.html) != 'undefined') {
33915             txt = n.attributes.html;
33916         }
33917         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33918         var cb = typeof a.checked == 'boolean';
33919         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33920         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33921             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33922             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33923             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33924             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33925             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33926              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33927                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33928             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33929             "</li>"];
33930
33931         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33932             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33933                                 n.nextSibling.ui.getEl(), buf.join(""));
33934         }else{
33935             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33936         }
33937
33938         this.elNode = this.wrap.childNodes[0];
33939         this.ctNode = this.wrap.childNodes[1];
33940         var cs = this.elNode.childNodes;
33941         this.indentNode = cs[0];
33942         this.ecNode = cs[1];
33943         this.iconNode = cs[2];
33944         var index = 3;
33945         if(cb){
33946             this.checkbox = cs[3];
33947             index++;
33948         }
33949         this.anchor = cs[index];
33950         this.textNode = cs[index].firstChild;
33951     },
33952
33953     getAnchor : function(){
33954         return this.anchor;
33955     },
33956
33957     getTextEl : function(){
33958         return this.textNode;
33959     },
33960
33961     getIconEl : function(){
33962         return this.iconNode;
33963     },
33964
33965     isChecked : function(){
33966         return this.checkbox ? this.checkbox.checked : false;
33967     },
33968
33969     updateExpandIcon : function(){
33970         if(this.rendered){
33971             var n = this.node, c1, c2;
33972             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33973             var hasChild = n.hasChildNodes();
33974             if(hasChild){
33975                 if(n.expanded){
33976                     cls += "-minus";
33977                     c1 = "x-tree-node-collapsed";
33978                     c2 = "x-tree-node-expanded";
33979                 }else{
33980                     cls += "-plus";
33981                     c1 = "x-tree-node-expanded";
33982                     c2 = "x-tree-node-collapsed";
33983                 }
33984                 if(this.wasLeaf){
33985                     this.removeClass("x-tree-node-leaf");
33986                     this.wasLeaf = false;
33987                 }
33988                 if(this.c1 != c1 || this.c2 != c2){
33989                     Roo.fly(this.elNode).replaceClass(c1, c2);
33990                     this.c1 = c1; this.c2 = c2;
33991                 }
33992             }else{
33993                 // this changes non-leafs into leafs if they have no children.
33994                 // it's not very rational behaviour..
33995                 
33996                 if(!this.wasLeaf && this.node.leaf){
33997                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33998                     delete this.c1;
33999                     delete this.c2;
34000                     this.wasLeaf = true;
34001                 }
34002             }
34003             var ecc = "x-tree-ec-icon "+cls;
34004             if(this.ecc != ecc){
34005                 this.ecNode.className = ecc;
34006                 this.ecc = ecc;
34007             }
34008         }
34009     },
34010
34011     getChildIndent : function(){
34012         if(!this.childIndent){
34013             var buf = [];
34014             var p = this.node;
34015             while(p){
34016                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34017                     if(!p.isLast()) {
34018                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34019                     } else {
34020                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34021                     }
34022                 }
34023                 p = p.parentNode;
34024             }
34025             this.childIndent = buf.join("");
34026         }
34027         return this.childIndent;
34028     },
34029
34030     renderIndent : function(){
34031         if(this.rendered){
34032             var indent = "";
34033             var p = this.node.parentNode;
34034             if(p){
34035                 indent = p.ui.getChildIndent();
34036             }
34037             if(this.indentMarkup != indent){ // don't rerender if not required
34038                 this.indentNode.innerHTML = indent;
34039                 this.indentMarkup = indent;
34040             }
34041             this.updateExpandIcon();
34042         }
34043     }
34044 };
34045
34046 Roo.tree.RootTreeNodeUI = function(){
34047     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34048 };
34049 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34050     render : function(){
34051         if(!this.rendered){
34052             var targetNode = this.node.ownerTree.innerCt.dom;
34053             this.node.expanded = true;
34054             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34055             this.wrap = this.ctNode = targetNode.firstChild;
34056         }
34057     },
34058     collapse : function(){
34059     },
34060     expand : function(){
34061     }
34062 });/*
34063  * Based on:
34064  * Ext JS Library 1.1.1
34065  * Copyright(c) 2006-2007, Ext JS, LLC.
34066  *
34067  * Originally Released Under LGPL - original licence link has changed is not relivant.
34068  *
34069  * Fork - LGPL
34070  * <script type="text/javascript">
34071  */
34072 /**
34073  * @class Roo.tree.TreeLoader
34074  * @extends Roo.util.Observable
34075  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34076  * nodes from a specified URL. The response must be a javascript Array definition
34077  * who's elements are node definition objects. eg:
34078  * <pre><code>
34079 {  success : true,
34080    data :      [
34081    
34082     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34083     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34084     ]
34085 }
34086
34087
34088 </code></pre>
34089  * <br><br>
34090  * The old style respose with just an array is still supported, but not recommended.
34091  * <br><br>
34092  *
34093  * A server request is sent, and child nodes are loaded only when a node is expanded.
34094  * The loading node's id is passed to the server under the parameter name "node" to
34095  * enable the server to produce the correct child nodes.
34096  * <br><br>
34097  * To pass extra parameters, an event handler may be attached to the "beforeload"
34098  * event, and the parameters specified in the TreeLoader's baseParams property:
34099  * <pre><code>
34100     myTreeLoader.on("beforeload", function(treeLoader, node) {
34101         this.baseParams.category = node.attributes.category;
34102     }, this);
34103 </code></pre><
34104  * This would pass an HTTP parameter called "category" to the server containing
34105  * the value of the Node's "category" attribute.
34106  * @constructor
34107  * Creates a new Treeloader.
34108  * @param {Object} config A config object containing config properties.
34109  */
34110 Roo.tree.TreeLoader = function(config){
34111     this.baseParams = {};
34112     this.requestMethod = "POST";
34113     Roo.apply(this, config);
34114
34115     this.addEvents({
34116     
34117         /**
34118          * @event beforeload
34119          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34120          * @param {Object} This TreeLoader object.
34121          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34122          * @param {Object} callback The callback function specified in the {@link #load} call.
34123          */
34124         beforeload : true,
34125         /**
34126          * @event load
34127          * Fires when the node has been successfuly loaded.
34128          * @param {Object} This TreeLoader object.
34129          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34130          * @param {Object} response The response object containing the data from the server.
34131          */
34132         load : true,
34133         /**
34134          * @event loadexception
34135          * Fires if the network request failed.
34136          * @param {Object} This TreeLoader object.
34137          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34138          * @param {Object} response The response object containing the data from the server.
34139          */
34140         loadexception : true,
34141         /**
34142          * @event create
34143          * Fires before a node is created, enabling you to return custom Node types 
34144          * @param {Object} This TreeLoader object.
34145          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34146          */
34147         create : true
34148     });
34149
34150     Roo.tree.TreeLoader.superclass.constructor.call(this);
34151 };
34152
34153 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34154     /**
34155     * @cfg {String} dataUrl The URL from which to request a Json string which
34156     * specifies an array of node definition object representing the child nodes
34157     * to be loaded.
34158     */
34159     /**
34160     * @cfg {String} requestMethod either GET or POST
34161     * defaults to POST (due to BC)
34162     * to be loaded.
34163     */
34164     /**
34165     * @cfg {Object} baseParams (optional) An object containing properties which
34166     * specify HTTP parameters to be passed to each request for child nodes.
34167     */
34168     /**
34169     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34170     * created by this loader. If the attributes sent by the server have an attribute in this object,
34171     * they take priority.
34172     */
34173     /**
34174     * @cfg {Object} uiProviders (optional) An object containing properties which
34175     * 
34176     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34177     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34178     * <i>uiProvider</i> attribute of a returned child node is a string rather
34179     * than a reference to a TreeNodeUI implementation, this that string value
34180     * is used as a property name in the uiProviders object. You can define the provider named
34181     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34182     */
34183     uiProviders : {},
34184
34185     /**
34186     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34187     * child nodes before loading.
34188     */
34189     clearOnLoad : true,
34190
34191     /**
34192     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34193     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34194     * Grid query { data : [ .....] }
34195     */
34196     
34197     root : false,
34198      /**
34199     * @cfg {String} queryParam (optional) 
34200     * Name of the query as it will be passed on the querystring (defaults to 'node')
34201     * eg. the request will be ?node=[id]
34202     */
34203     
34204     
34205     queryParam: false,
34206     
34207     /**
34208      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34209      * This is called automatically when a node is expanded, but may be used to reload
34210      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34211      * @param {Roo.tree.TreeNode} node
34212      * @param {Function} callback
34213      */
34214     load : function(node, callback){
34215         if(this.clearOnLoad){
34216             while(node.firstChild){
34217                 node.removeChild(node.firstChild);
34218             }
34219         }
34220         if(node.attributes.children){ // preloaded json children
34221             var cs = node.attributes.children;
34222             for(var i = 0, len = cs.length; i < len; i++){
34223                 node.appendChild(this.createNode(cs[i]));
34224             }
34225             if(typeof callback == "function"){
34226                 callback();
34227             }
34228         }else if(this.dataUrl){
34229             this.requestData(node, callback);
34230         }
34231     },
34232
34233     getParams: function(node){
34234         var buf = [], bp = this.baseParams;
34235         for(var key in bp){
34236             if(typeof bp[key] != "function"){
34237                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34238             }
34239         }
34240         var n = this.queryParam === false ? 'node' : this.queryParam;
34241         buf.push(n + "=", encodeURIComponent(node.id));
34242         return buf.join("");
34243     },
34244
34245     requestData : function(node, callback){
34246         if(this.fireEvent("beforeload", this, node, callback) !== false){
34247             this.transId = Roo.Ajax.request({
34248                 method:this.requestMethod,
34249                 url: this.dataUrl||this.url,
34250                 success: this.handleResponse,
34251                 failure: this.handleFailure,
34252                 scope: this,
34253                 argument: {callback: callback, node: node},
34254                 params: this.getParams(node)
34255             });
34256         }else{
34257             // if the load is cancelled, make sure we notify
34258             // the node that we are done
34259             if(typeof callback == "function"){
34260                 callback();
34261             }
34262         }
34263     },
34264
34265     isLoading : function(){
34266         return this.transId ? true : false;
34267     },
34268
34269     abort : function(){
34270         if(this.isLoading()){
34271             Roo.Ajax.abort(this.transId);
34272         }
34273     },
34274
34275     // private
34276     createNode : function(attr)
34277     {
34278         // apply baseAttrs, nice idea Corey!
34279         if(this.baseAttrs){
34280             Roo.applyIf(attr, this.baseAttrs);
34281         }
34282         if(this.applyLoader !== false){
34283             attr.loader = this;
34284         }
34285         // uiProvider = depreciated..
34286         
34287         if(typeof(attr.uiProvider) == 'string'){
34288            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34289                 /**  eval:var:attr */ eval(attr.uiProvider);
34290         }
34291         if(typeof(this.uiProviders['default']) != 'undefined') {
34292             attr.uiProvider = this.uiProviders['default'];
34293         }
34294         
34295         this.fireEvent('create', this, attr);
34296         
34297         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34298         return(attr.leaf ?
34299                         new Roo.tree.TreeNode(attr) :
34300                         new Roo.tree.AsyncTreeNode(attr));
34301     },
34302
34303     processResponse : function(response, node, callback)
34304     {
34305         var json = response.responseText;
34306         try {
34307             
34308             var o = Roo.decode(json);
34309             
34310             if (this.root === false && typeof(o.success) != undefined) {
34311                 this.root = 'data'; // the default behaviour for list like data..
34312                 }
34313                 
34314             if (this.root !== false &&  !o.success) {
34315                 // it's a failure condition.
34316                 var a = response.argument;
34317                 this.fireEvent("loadexception", this, a.node, response);
34318                 Roo.log("Load failed - should have a handler really");
34319                 return;
34320             }
34321             
34322             
34323             
34324             if (this.root !== false) {
34325                  o = o[this.root];
34326             }
34327             
34328             for(var i = 0, len = o.length; i < len; i++){
34329                 var n = this.createNode(o[i]);
34330                 if(n){
34331                     node.appendChild(n);
34332                 }
34333             }
34334             if(typeof callback == "function"){
34335                 callback(this, node);
34336             }
34337         }catch(e){
34338             this.handleFailure(response);
34339         }
34340     },
34341
34342     handleResponse : function(response){
34343         this.transId = false;
34344         var a = response.argument;
34345         this.processResponse(response, a.node, a.callback);
34346         this.fireEvent("load", this, a.node, response);
34347     },
34348
34349     handleFailure : function(response)
34350     {
34351         // should handle failure better..
34352         this.transId = false;
34353         var a = response.argument;
34354         this.fireEvent("loadexception", this, a.node, response);
34355         if(typeof a.callback == "function"){
34356             a.callback(this, a.node);
34357         }
34358     }
34359 });/*
34360  * Based on:
34361  * Ext JS Library 1.1.1
34362  * Copyright(c) 2006-2007, Ext JS, LLC.
34363  *
34364  * Originally Released Under LGPL - original licence link has changed is not relivant.
34365  *
34366  * Fork - LGPL
34367  * <script type="text/javascript">
34368  */
34369
34370 /**
34371 * @class Roo.tree.TreeFilter
34372 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34373 * @param {TreePanel} tree
34374 * @param {Object} config (optional)
34375  */
34376 Roo.tree.TreeFilter = function(tree, config){
34377     this.tree = tree;
34378     this.filtered = {};
34379     Roo.apply(this, config);
34380 };
34381
34382 Roo.tree.TreeFilter.prototype = {
34383     clearBlank:false,
34384     reverse:false,
34385     autoClear:false,
34386     remove:false,
34387
34388      /**
34389      * Filter the data by a specific attribute.
34390      * @param {String/RegExp} value Either string that the attribute value
34391      * should start with or a RegExp to test against the attribute
34392      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34393      * @param {TreeNode} startNode (optional) The node to start the filter at.
34394      */
34395     filter : function(value, attr, startNode){
34396         attr = attr || "text";
34397         var f;
34398         if(typeof value == "string"){
34399             var vlen = value.length;
34400             // auto clear empty filter
34401             if(vlen == 0 && this.clearBlank){
34402                 this.clear();
34403                 return;
34404             }
34405             value = value.toLowerCase();
34406             f = function(n){
34407                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34408             };
34409         }else if(value.exec){ // regex?
34410             f = function(n){
34411                 return value.test(n.attributes[attr]);
34412             };
34413         }else{
34414             throw 'Illegal filter type, must be string or regex';
34415         }
34416         this.filterBy(f, null, startNode);
34417         },
34418
34419     /**
34420      * Filter by a function. The passed function will be called with each
34421      * node in the tree (or from the startNode). If the function returns true, the node is kept
34422      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34423      * @param {Function} fn The filter function
34424      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34425      */
34426     filterBy : function(fn, scope, startNode){
34427         startNode = startNode || this.tree.root;
34428         if(this.autoClear){
34429             this.clear();
34430         }
34431         var af = this.filtered, rv = this.reverse;
34432         var f = function(n){
34433             if(n == startNode){
34434                 return true;
34435             }
34436             if(af[n.id]){
34437                 return false;
34438             }
34439             var m = fn.call(scope || n, n);
34440             if(!m || rv){
34441                 af[n.id] = n;
34442                 n.ui.hide();
34443                 return false;
34444             }
34445             return true;
34446         };
34447         startNode.cascade(f);
34448         if(this.remove){
34449            for(var id in af){
34450                if(typeof id != "function"){
34451                    var n = af[id];
34452                    if(n && n.parentNode){
34453                        n.parentNode.removeChild(n);
34454                    }
34455                }
34456            }
34457         }
34458     },
34459
34460     /**
34461      * Clears the current filter. Note: with the "remove" option
34462      * set a filter cannot be cleared.
34463      */
34464     clear : function(){
34465         var t = this.tree;
34466         var af = this.filtered;
34467         for(var id in af){
34468             if(typeof id != "function"){
34469                 var n = af[id];
34470                 if(n){
34471                     n.ui.show();
34472                 }
34473             }
34474         }
34475         this.filtered = {};
34476     }
34477 };
34478 /*
34479  * Based on:
34480  * Ext JS Library 1.1.1
34481  * Copyright(c) 2006-2007, Ext JS, LLC.
34482  *
34483  * Originally Released Under LGPL - original licence link has changed is not relivant.
34484  *
34485  * Fork - LGPL
34486  * <script type="text/javascript">
34487  */
34488  
34489
34490 /**
34491  * @class Roo.tree.TreeSorter
34492  * Provides sorting of nodes in a TreePanel
34493  * 
34494  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34495  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34496  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34497  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34498  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34499  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34500  * @constructor
34501  * @param {TreePanel} tree
34502  * @param {Object} config
34503  */
34504 Roo.tree.TreeSorter = function(tree, config){
34505     Roo.apply(this, config);
34506     tree.on("beforechildrenrendered", this.doSort, this);
34507     tree.on("append", this.updateSort, this);
34508     tree.on("insert", this.updateSort, this);
34509     
34510     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34511     var p = this.property || "text";
34512     var sortType = this.sortType;
34513     var fs = this.folderSort;
34514     var cs = this.caseSensitive === true;
34515     var leafAttr = this.leafAttr || 'leaf';
34516
34517     this.sortFn = function(n1, n2){
34518         if(fs){
34519             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34520                 return 1;
34521             }
34522             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34523                 return -1;
34524             }
34525         }
34526         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34527         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34528         if(v1 < v2){
34529                         return dsc ? +1 : -1;
34530                 }else if(v1 > v2){
34531                         return dsc ? -1 : +1;
34532         }else{
34533                 return 0;
34534         }
34535     };
34536 };
34537
34538 Roo.tree.TreeSorter.prototype = {
34539     doSort : function(node){
34540         node.sort(this.sortFn);
34541     },
34542     
34543     compareNodes : function(n1, n2){
34544         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34545     },
34546     
34547     updateSort : function(tree, node){
34548         if(node.childrenRendered){
34549             this.doSort.defer(1, this, [node]);
34550         }
34551     }
34552 };/*
34553  * Based on:
34554  * Ext JS Library 1.1.1
34555  * Copyright(c) 2006-2007, Ext JS, LLC.
34556  *
34557  * Originally Released Under LGPL - original licence link has changed is not relivant.
34558  *
34559  * Fork - LGPL
34560  * <script type="text/javascript">
34561  */
34562
34563 if(Roo.dd.DropZone){
34564     
34565 Roo.tree.TreeDropZone = function(tree, config){
34566     this.allowParentInsert = false;
34567     this.allowContainerDrop = false;
34568     this.appendOnly = false;
34569     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34570     this.tree = tree;
34571     this.lastInsertClass = "x-tree-no-status";
34572     this.dragOverData = {};
34573 };
34574
34575 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34576     ddGroup : "TreeDD",
34577     scroll:  true,
34578     
34579     expandDelay : 1000,
34580     
34581     expandNode : function(node){
34582         if(node.hasChildNodes() && !node.isExpanded()){
34583             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34584         }
34585     },
34586     
34587     queueExpand : function(node){
34588         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34589     },
34590     
34591     cancelExpand : function(){
34592         if(this.expandProcId){
34593             clearTimeout(this.expandProcId);
34594             this.expandProcId = false;
34595         }
34596     },
34597     
34598     isValidDropPoint : function(n, pt, dd, e, data){
34599         if(!n || !data){ return false; }
34600         var targetNode = n.node;
34601         var dropNode = data.node;
34602         // default drop rules
34603         if(!(targetNode && targetNode.isTarget && pt)){
34604             return false;
34605         }
34606         if(pt == "append" && targetNode.allowChildren === false){
34607             return false;
34608         }
34609         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34610             return false;
34611         }
34612         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34613             return false;
34614         }
34615         // reuse the object
34616         var overEvent = this.dragOverData;
34617         overEvent.tree = this.tree;
34618         overEvent.target = targetNode;
34619         overEvent.data = data;
34620         overEvent.point = pt;
34621         overEvent.source = dd;
34622         overEvent.rawEvent = e;
34623         overEvent.dropNode = dropNode;
34624         overEvent.cancel = false;  
34625         var result = this.tree.fireEvent("nodedragover", overEvent);
34626         return overEvent.cancel === false && result !== false;
34627     },
34628     
34629     getDropPoint : function(e, n, dd)
34630     {
34631         var tn = n.node;
34632         if(tn.isRoot){
34633             return tn.allowChildren !== false ? "append" : false; // always append for root
34634         }
34635         var dragEl = n.ddel;
34636         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34637         var y = Roo.lib.Event.getPageY(e);
34638         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34639         
34640         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34641         var noAppend = tn.allowChildren === false;
34642         if(this.appendOnly || tn.parentNode.allowChildren === false){
34643             return noAppend ? false : "append";
34644         }
34645         var noBelow = false;
34646         if(!this.allowParentInsert){
34647             noBelow = tn.hasChildNodes() && tn.isExpanded();
34648         }
34649         var q = (b - t) / (noAppend ? 2 : 3);
34650         if(y >= t && y < (t + q)){
34651             return "above";
34652         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34653             return "below";
34654         }else{
34655             return "append";
34656         }
34657     },
34658     
34659     onNodeEnter : function(n, dd, e, data)
34660     {
34661         this.cancelExpand();
34662     },
34663     
34664     onNodeOver : function(n, dd, e, data)
34665     {
34666        
34667         var pt = this.getDropPoint(e, n, dd);
34668         var node = n.node;
34669         
34670         // auto node expand check
34671         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34672             this.queueExpand(node);
34673         }else if(pt != "append"){
34674             this.cancelExpand();
34675         }
34676         
34677         // set the insert point style on the target node
34678         var returnCls = this.dropNotAllowed;
34679         if(this.isValidDropPoint(n, pt, dd, e, data)){
34680            if(pt){
34681                var el = n.ddel;
34682                var cls;
34683                if(pt == "above"){
34684                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34685                    cls = "x-tree-drag-insert-above";
34686                }else if(pt == "below"){
34687                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34688                    cls = "x-tree-drag-insert-below";
34689                }else{
34690                    returnCls = "x-tree-drop-ok-append";
34691                    cls = "x-tree-drag-append";
34692                }
34693                if(this.lastInsertClass != cls){
34694                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34695                    this.lastInsertClass = cls;
34696                }
34697            }
34698        }
34699        return returnCls;
34700     },
34701     
34702     onNodeOut : function(n, dd, e, data){
34703         
34704         this.cancelExpand();
34705         this.removeDropIndicators(n);
34706     },
34707     
34708     onNodeDrop : function(n, dd, e, data){
34709         var point = this.getDropPoint(e, n, dd);
34710         var targetNode = n.node;
34711         targetNode.ui.startDrop();
34712         if(!this.isValidDropPoint(n, point, dd, e, data)){
34713             targetNode.ui.endDrop();
34714             return false;
34715         }
34716         // first try to find the drop node
34717         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34718         var dropEvent = {
34719             tree : this.tree,
34720             target: targetNode,
34721             data: data,
34722             point: point,
34723             source: dd,
34724             rawEvent: e,
34725             dropNode: dropNode,
34726             cancel: !dropNode   
34727         };
34728         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34729         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34730             targetNode.ui.endDrop();
34731             return false;
34732         }
34733         // allow target changing
34734         targetNode = dropEvent.target;
34735         if(point == "append" && !targetNode.isExpanded()){
34736             targetNode.expand(false, null, function(){
34737                 this.completeDrop(dropEvent);
34738             }.createDelegate(this));
34739         }else{
34740             this.completeDrop(dropEvent);
34741         }
34742         return true;
34743     },
34744     
34745     completeDrop : function(de){
34746         var ns = de.dropNode, p = de.point, t = de.target;
34747         if(!(ns instanceof Array)){
34748             ns = [ns];
34749         }
34750         var n;
34751         for(var i = 0, len = ns.length; i < len; i++){
34752             n = ns[i];
34753             if(p == "above"){
34754                 t.parentNode.insertBefore(n, t);
34755             }else if(p == "below"){
34756                 t.parentNode.insertBefore(n, t.nextSibling);
34757             }else{
34758                 t.appendChild(n);
34759             }
34760         }
34761         n.ui.focus();
34762         if(this.tree.hlDrop){
34763             n.ui.highlight();
34764         }
34765         t.ui.endDrop();
34766         this.tree.fireEvent("nodedrop", de);
34767     },
34768     
34769     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34770         if(this.tree.hlDrop){
34771             dropNode.ui.focus();
34772             dropNode.ui.highlight();
34773         }
34774         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34775     },
34776     
34777     getTree : function(){
34778         return this.tree;
34779     },
34780     
34781     removeDropIndicators : function(n){
34782         if(n && n.ddel){
34783             var el = n.ddel;
34784             Roo.fly(el).removeClass([
34785                     "x-tree-drag-insert-above",
34786                     "x-tree-drag-insert-below",
34787                     "x-tree-drag-append"]);
34788             this.lastInsertClass = "_noclass";
34789         }
34790     },
34791     
34792     beforeDragDrop : function(target, e, id){
34793         this.cancelExpand();
34794         return true;
34795     },
34796     
34797     afterRepair : function(data){
34798         if(data && Roo.enableFx){
34799             data.node.ui.highlight();
34800         }
34801         this.hideProxy();
34802     } 
34803     
34804 });
34805
34806 }
34807 /*
34808  * Based on:
34809  * Ext JS Library 1.1.1
34810  * Copyright(c) 2006-2007, Ext JS, LLC.
34811  *
34812  * Originally Released Under LGPL - original licence link has changed is not relivant.
34813  *
34814  * Fork - LGPL
34815  * <script type="text/javascript">
34816  */
34817  
34818
34819 if(Roo.dd.DragZone){
34820 Roo.tree.TreeDragZone = function(tree, config){
34821     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34822     this.tree = tree;
34823 };
34824
34825 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34826     ddGroup : "TreeDD",
34827    
34828     onBeforeDrag : function(data, e){
34829         var n = data.node;
34830         return n && n.draggable && !n.disabled;
34831     },
34832      
34833     
34834     onInitDrag : function(e){
34835         var data = this.dragData;
34836         this.tree.getSelectionModel().select(data.node);
34837         this.proxy.update("");
34838         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34839         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34840     },
34841     
34842     getRepairXY : function(e, data){
34843         return data.node.ui.getDDRepairXY();
34844     },
34845     
34846     onEndDrag : function(data, e){
34847         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34848         
34849         
34850     },
34851     
34852     onValidDrop : function(dd, e, id){
34853         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34854         this.hideProxy();
34855     },
34856     
34857     beforeInvalidDrop : function(e, id){
34858         // this scrolls the original position back into view
34859         var sm = this.tree.getSelectionModel();
34860         sm.clearSelections();
34861         sm.select(this.dragData.node);
34862     }
34863 });
34864 }/*
34865  * Based on:
34866  * Ext JS Library 1.1.1
34867  * Copyright(c) 2006-2007, Ext JS, LLC.
34868  *
34869  * Originally Released Under LGPL - original licence link has changed is not relivant.
34870  *
34871  * Fork - LGPL
34872  * <script type="text/javascript">
34873  */
34874 /**
34875  * @class Roo.tree.TreeEditor
34876  * @extends Roo.Editor
34877  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34878  * as the editor field.
34879  * @constructor
34880  * @param {Object} config (used to be the tree panel.)
34881  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34882  * 
34883  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34884  * @cfg {Roo.form.TextField|Object} field The field configuration
34885  *
34886  * 
34887  */
34888 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34889     var tree = config;
34890     var field;
34891     if (oldconfig) { // old style..
34892         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34893     } else {
34894         // new style..
34895         tree = config.tree;
34896         config.field = config.field  || {};
34897         config.field.xtype = 'TextField';
34898         field = Roo.factory(config.field, Roo.form);
34899     }
34900     config = config || {};
34901     
34902     
34903     this.addEvents({
34904         /**
34905          * @event beforenodeedit
34906          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34907          * false from the handler of this event.
34908          * @param {Editor} this
34909          * @param {Roo.tree.Node} node 
34910          */
34911         "beforenodeedit" : true
34912     });
34913     
34914     //Roo.log(config);
34915     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34916
34917     this.tree = tree;
34918
34919     tree.on('beforeclick', this.beforeNodeClick, this);
34920     tree.getTreeEl().on('mousedown', this.hide, this);
34921     this.on('complete', this.updateNode, this);
34922     this.on('beforestartedit', this.fitToTree, this);
34923     this.on('startedit', this.bindScroll, this, {delay:10});
34924     this.on('specialkey', this.onSpecialKey, this);
34925 };
34926
34927 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34928     /**
34929      * @cfg {String} alignment
34930      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34931      */
34932     alignment: "l-l",
34933     // inherit
34934     autoSize: false,
34935     /**
34936      * @cfg {Boolean} hideEl
34937      * True to hide the bound element while the editor is displayed (defaults to false)
34938      */
34939     hideEl : false,
34940     /**
34941      * @cfg {String} cls
34942      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34943      */
34944     cls: "x-small-editor x-tree-editor",
34945     /**
34946      * @cfg {Boolean} shim
34947      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34948      */
34949     shim:false,
34950     // inherit
34951     shadow:"frame",
34952     /**
34953      * @cfg {Number} maxWidth
34954      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34955      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34956      * scroll and client offsets into account prior to each edit.
34957      */
34958     maxWidth: 250,
34959
34960     editDelay : 350,
34961
34962     // private
34963     fitToTree : function(ed, el){
34964         var td = this.tree.getTreeEl().dom, nd = el.dom;
34965         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34966             td.scrollLeft = nd.offsetLeft;
34967         }
34968         var w = Math.min(
34969                 this.maxWidth,
34970                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34971         this.setSize(w, '');
34972         
34973         return this.fireEvent('beforenodeedit', this, this.editNode);
34974         
34975     },
34976
34977     // private
34978     triggerEdit : function(node){
34979         this.completeEdit();
34980         this.editNode = node;
34981         this.startEdit(node.ui.textNode, node.text);
34982     },
34983
34984     // private
34985     bindScroll : function(){
34986         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34987     },
34988
34989     // private
34990     beforeNodeClick : function(node, e){
34991         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34992         this.lastClick = new Date();
34993         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34994             e.stopEvent();
34995             this.triggerEdit(node);
34996             return false;
34997         }
34998         return true;
34999     },
35000
35001     // private
35002     updateNode : function(ed, value){
35003         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35004         this.editNode.setText(value);
35005     },
35006
35007     // private
35008     onHide : function(){
35009         Roo.tree.TreeEditor.superclass.onHide.call(this);
35010         if(this.editNode){
35011             this.editNode.ui.focus();
35012         }
35013     },
35014
35015     // private
35016     onSpecialKey : function(field, e){
35017         var k = e.getKey();
35018         if(k == e.ESC){
35019             e.stopEvent();
35020             this.cancelEdit();
35021         }else if(k == e.ENTER && !e.hasModifier()){
35022             e.stopEvent();
35023             this.completeEdit();
35024         }
35025     }
35026 });//<Script type="text/javascript">
35027 /*
35028  * Based on:
35029  * Ext JS Library 1.1.1
35030  * Copyright(c) 2006-2007, Ext JS, LLC.
35031  *
35032  * Originally Released Under LGPL - original licence link has changed is not relivant.
35033  *
35034  * Fork - LGPL
35035  * <script type="text/javascript">
35036  */
35037  
35038 /**
35039  * Not documented??? - probably should be...
35040  */
35041
35042 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35043     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35044     
35045     renderElements : function(n, a, targetNode, bulkRender){
35046         //consel.log("renderElements?");
35047         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35048
35049         var t = n.getOwnerTree();
35050         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35051         
35052         var cols = t.columns;
35053         var bw = t.borderWidth;
35054         var c = cols[0];
35055         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35056          var cb = typeof a.checked == "boolean";
35057         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35058         var colcls = 'x-t-' + tid + '-c0';
35059         var buf = [
35060             '<li class="x-tree-node">',
35061             
35062                 
35063                 '<div class="x-tree-node-el ', a.cls,'">',
35064                     // extran...
35065                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35066                 
35067                 
35068                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35069                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35070                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35071                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35072                            (a.iconCls ? ' '+a.iconCls : ''),
35073                            '" unselectable="on" />',
35074                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35075                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35076                              
35077                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35078                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35079                             '<span unselectable="on" qtip="' + tx + '">',
35080                              tx,
35081                              '</span></a>' ,
35082                     '</div>',
35083                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35084                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35085                  ];
35086         for(var i = 1, len = cols.length; i < len; i++){
35087             c = cols[i];
35088             colcls = 'x-t-' + tid + '-c' +i;
35089             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35090             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35091                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35092                       "</div>");
35093          }
35094          
35095          buf.push(
35096             '</a>',
35097             '<div class="x-clear"></div></div>',
35098             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35099             "</li>");
35100         
35101         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35102             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35103                                 n.nextSibling.ui.getEl(), buf.join(""));
35104         }else{
35105             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35106         }
35107         var el = this.wrap.firstChild;
35108         this.elRow = el;
35109         this.elNode = el.firstChild;
35110         this.ranchor = el.childNodes[1];
35111         this.ctNode = this.wrap.childNodes[1];
35112         var cs = el.firstChild.childNodes;
35113         this.indentNode = cs[0];
35114         this.ecNode = cs[1];
35115         this.iconNode = cs[2];
35116         var index = 3;
35117         if(cb){
35118             this.checkbox = cs[3];
35119             index++;
35120         }
35121         this.anchor = cs[index];
35122         
35123         this.textNode = cs[index].firstChild;
35124         
35125         //el.on("click", this.onClick, this);
35126         //el.on("dblclick", this.onDblClick, this);
35127         
35128         
35129        // console.log(this);
35130     },
35131     initEvents : function(){
35132         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35133         
35134             
35135         var a = this.ranchor;
35136
35137         var el = Roo.get(a);
35138
35139         if(Roo.isOpera){ // opera render bug ignores the CSS
35140             el.setStyle("text-decoration", "none");
35141         }
35142
35143         el.on("click", this.onClick, this);
35144         el.on("dblclick", this.onDblClick, this);
35145         el.on("contextmenu", this.onContextMenu, this);
35146         
35147     },
35148     
35149     /*onSelectedChange : function(state){
35150         if(state){
35151             this.focus();
35152             this.addClass("x-tree-selected");
35153         }else{
35154             //this.blur();
35155             this.removeClass("x-tree-selected");
35156         }
35157     },*/
35158     addClass : function(cls){
35159         if(this.elRow){
35160             Roo.fly(this.elRow).addClass(cls);
35161         }
35162         
35163     },
35164     
35165     
35166     removeClass : function(cls){
35167         if(this.elRow){
35168             Roo.fly(this.elRow).removeClass(cls);
35169         }
35170     }
35171
35172     
35173     
35174 });//<Script type="text/javascript">
35175
35176 /*
35177  * Based on:
35178  * Ext JS Library 1.1.1
35179  * Copyright(c) 2006-2007, Ext JS, LLC.
35180  *
35181  * Originally Released Under LGPL - original licence link has changed is not relivant.
35182  *
35183  * Fork - LGPL
35184  * <script type="text/javascript">
35185  */
35186  
35187
35188 /**
35189  * @class Roo.tree.ColumnTree
35190  * @extends Roo.data.TreePanel
35191  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35192  * @cfg {int} borderWidth  compined right/left border allowance
35193  * @constructor
35194  * @param {String/HTMLElement/Element} el The container element
35195  * @param {Object} config
35196  */
35197 Roo.tree.ColumnTree =  function(el, config)
35198 {
35199    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35200    this.addEvents({
35201         /**
35202         * @event resize
35203         * Fire this event on a container when it resizes
35204         * @param {int} w Width
35205         * @param {int} h Height
35206         */
35207        "resize" : true
35208     });
35209     this.on('resize', this.onResize, this);
35210 };
35211
35212 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35213     //lines:false,
35214     
35215     
35216     borderWidth: Roo.isBorderBox ? 0 : 2, 
35217     headEls : false,
35218     
35219     render : function(){
35220         // add the header.....
35221        
35222         Roo.tree.ColumnTree.superclass.render.apply(this);
35223         
35224         this.el.addClass('x-column-tree');
35225         
35226         this.headers = this.el.createChild(
35227             {cls:'x-tree-headers'},this.innerCt.dom);
35228    
35229         var cols = this.columns, c;
35230         var totalWidth = 0;
35231         this.headEls = [];
35232         var  len = cols.length;
35233         for(var i = 0; i < len; i++){
35234              c = cols[i];
35235              totalWidth += c.width;
35236             this.headEls.push(this.headers.createChild({
35237                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35238                  cn: {
35239                      cls:'x-tree-hd-text',
35240                      html: c.header
35241                  },
35242                  style:'width:'+(c.width-this.borderWidth)+'px;'
35243              }));
35244         }
35245         this.headers.createChild({cls:'x-clear'});
35246         // prevent floats from wrapping when clipped
35247         this.headers.setWidth(totalWidth);
35248         //this.innerCt.setWidth(totalWidth);
35249         this.innerCt.setStyle({ overflow: 'auto' });
35250         this.onResize(this.width, this.height);
35251              
35252         
35253     },
35254     onResize : function(w,h)
35255     {
35256         this.height = h;
35257         this.width = w;
35258         // resize cols..
35259         this.innerCt.setWidth(this.width);
35260         this.innerCt.setHeight(this.height-20);
35261         
35262         // headers...
35263         var cols = this.columns, c;
35264         var totalWidth = 0;
35265         var expEl = false;
35266         var len = cols.length;
35267         for(var i = 0; i < len; i++){
35268             c = cols[i];
35269             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35270                 // it's the expander..
35271                 expEl  = this.headEls[i];
35272                 continue;
35273             }
35274             totalWidth += c.width;
35275             
35276         }
35277         if (expEl) {
35278             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35279         }
35280         this.headers.setWidth(w-20);
35281
35282         
35283         
35284         
35285     }
35286 });
35287 /*
35288  * Based on:
35289  * Ext JS Library 1.1.1
35290  * Copyright(c) 2006-2007, Ext JS, LLC.
35291  *
35292  * Originally Released Under LGPL - original licence link has changed is not relivant.
35293  *
35294  * Fork - LGPL
35295  * <script type="text/javascript">
35296  */
35297  
35298 /**
35299  * @class Roo.menu.Menu
35300  * @extends Roo.util.Observable
35301  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35302  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35303  * @constructor
35304  * Creates a new Menu
35305  * @param {Object} config Configuration options
35306  */
35307 Roo.menu.Menu = function(config){
35308     Roo.apply(this, config);
35309     this.id = this.id || Roo.id();
35310     this.addEvents({
35311         /**
35312          * @event beforeshow
35313          * Fires before this menu is displayed
35314          * @param {Roo.menu.Menu} this
35315          */
35316         beforeshow : true,
35317         /**
35318          * @event beforehide
35319          * Fires before this menu is hidden
35320          * @param {Roo.menu.Menu} this
35321          */
35322         beforehide : true,
35323         /**
35324          * @event show
35325          * Fires after this menu is displayed
35326          * @param {Roo.menu.Menu} this
35327          */
35328         show : true,
35329         /**
35330          * @event hide
35331          * Fires after this menu is hidden
35332          * @param {Roo.menu.Menu} this
35333          */
35334         hide : true,
35335         /**
35336          * @event click
35337          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35338          * @param {Roo.menu.Menu} this
35339          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35340          * @param {Roo.EventObject} e
35341          */
35342         click : true,
35343         /**
35344          * @event mouseover
35345          * Fires when the mouse is hovering over this menu
35346          * @param {Roo.menu.Menu} this
35347          * @param {Roo.EventObject} e
35348          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35349          */
35350         mouseover : true,
35351         /**
35352          * @event mouseout
35353          * Fires when the mouse exits this menu
35354          * @param {Roo.menu.Menu} this
35355          * @param {Roo.EventObject} e
35356          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35357          */
35358         mouseout : true,
35359         /**
35360          * @event itemclick
35361          * Fires when a menu item contained in this menu is clicked
35362          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35363          * @param {Roo.EventObject} e
35364          */
35365         itemclick: true
35366     });
35367     if (this.registerMenu) {
35368         Roo.menu.MenuMgr.register(this);
35369     }
35370     
35371     var mis = this.items;
35372     this.items = new Roo.util.MixedCollection();
35373     if(mis){
35374         this.add.apply(this, mis);
35375     }
35376 };
35377
35378 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35379     /**
35380      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35381      */
35382     minWidth : 120,
35383     /**
35384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35385      * for bottom-right shadow (defaults to "sides")
35386      */
35387     shadow : "sides",
35388     /**
35389      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35390      * this menu (defaults to "tl-tr?")
35391      */
35392     subMenuAlign : "tl-tr?",
35393     /**
35394      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35395      * relative to its element of origin (defaults to "tl-bl?")
35396      */
35397     defaultAlign : "tl-bl?",
35398     /**
35399      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35400      */
35401     allowOtherMenus : false,
35402     /**
35403      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35404      */
35405     registerMenu : true,
35406
35407     hidden:true,
35408
35409     // private
35410     render : function(){
35411         if(this.el){
35412             return;
35413         }
35414         var el = this.el = new Roo.Layer({
35415             cls: "x-menu",
35416             shadow:this.shadow,
35417             constrain: false,
35418             parentEl: this.parentEl || document.body,
35419             zindex:15000
35420         });
35421
35422         this.keyNav = new Roo.menu.MenuNav(this);
35423
35424         if(this.plain){
35425             el.addClass("x-menu-plain");
35426         }
35427         if(this.cls){
35428             el.addClass(this.cls);
35429         }
35430         // generic focus element
35431         this.focusEl = el.createChild({
35432             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35433         });
35434         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35435         ul.on("click", this.onClick, this);
35436         ul.on("mouseover", this.onMouseOver, this);
35437         ul.on("mouseout", this.onMouseOut, this);
35438         this.items.each(function(item){
35439             if (item.hidden) {
35440                 return;
35441             }
35442             
35443             var li = document.createElement("li");
35444             li.className = "x-menu-list-item";
35445             ul.dom.appendChild(li);
35446             item.render(li, this);
35447         }, this);
35448         this.ul = ul;
35449         this.autoWidth();
35450     },
35451
35452     // private
35453     autoWidth : function(){
35454         var el = this.el, ul = this.ul;
35455         if(!el){
35456             return;
35457         }
35458         var w = this.width;
35459         if(w){
35460             el.setWidth(w);
35461         }else if(Roo.isIE){
35462             el.setWidth(this.minWidth);
35463             var t = el.dom.offsetWidth; // force recalc
35464             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35465         }
35466     },
35467
35468     // private
35469     delayAutoWidth : function(){
35470         if(this.rendered){
35471             if(!this.awTask){
35472                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35473             }
35474             this.awTask.delay(20);
35475         }
35476     },
35477
35478     // private
35479     findTargetItem : function(e){
35480         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35481         if(t && t.menuItemId){
35482             return this.items.get(t.menuItemId);
35483         }
35484     },
35485
35486     // private
35487     onClick : function(e){
35488         var t;
35489         if(t = this.findTargetItem(e)){
35490             t.onClick(e);
35491             this.fireEvent("click", this, t, e);
35492         }
35493     },
35494
35495     // private
35496     setActiveItem : function(item, autoExpand){
35497         if(item != this.activeItem){
35498             if(this.activeItem){
35499                 this.activeItem.deactivate();
35500             }
35501             this.activeItem = item;
35502             item.activate(autoExpand);
35503         }else if(autoExpand){
35504             item.expandMenu();
35505         }
35506     },
35507
35508     // private
35509     tryActivate : function(start, step){
35510         var items = this.items;
35511         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35512             var item = items.get(i);
35513             if(!item.disabled && item.canActivate){
35514                 this.setActiveItem(item, false);
35515                 return item;
35516             }
35517         }
35518         return false;
35519     },
35520
35521     // private
35522     onMouseOver : function(e){
35523         var t;
35524         if(t = this.findTargetItem(e)){
35525             if(t.canActivate && !t.disabled){
35526                 this.setActiveItem(t, true);
35527             }
35528         }
35529         this.fireEvent("mouseover", this, e, t);
35530     },
35531
35532     // private
35533     onMouseOut : function(e){
35534         var t;
35535         if(t = this.findTargetItem(e)){
35536             if(t == this.activeItem && t.shouldDeactivate(e)){
35537                 this.activeItem.deactivate();
35538                 delete this.activeItem;
35539             }
35540         }
35541         this.fireEvent("mouseout", this, e, t);
35542     },
35543
35544     /**
35545      * Read-only.  Returns true if the menu is currently displayed, else false.
35546      * @type Boolean
35547      */
35548     isVisible : function(){
35549         return this.el && !this.hidden;
35550     },
35551
35552     /**
35553      * Displays this menu relative to another element
35554      * @param {String/HTMLElement/Roo.Element} element The element to align to
35555      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35556      * the element (defaults to this.defaultAlign)
35557      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35558      */
35559     show : function(el, pos, parentMenu){
35560         this.parentMenu = parentMenu;
35561         if(!this.el){
35562             this.render();
35563         }
35564         this.fireEvent("beforeshow", this);
35565         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35566     },
35567
35568     /**
35569      * Displays this menu at a specific xy position
35570      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35571      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35572      */
35573     showAt : function(xy, parentMenu, /* private: */_e){
35574         this.parentMenu = parentMenu;
35575         if(!this.el){
35576             this.render();
35577         }
35578         if(_e !== false){
35579             this.fireEvent("beforeshow", this);
35580             xy = this.el.adjustForConstraints(xy);
35581         }
35582         this.el.setXY(xy);
35583         this.el.show();
35584         this.hidden = false;
35585         this.focus();
35586         this.fireEvent("show", this);
35587     },
35588
35589     focus : function(){
35590         if(!this.hidden){
35591             this.doFocus.defer(50, this);
35592         }
35593     },
35594
35595     doFocus : function(){
35596         if(!this.hidden){
35597             this.focusEl.focus();
35598         }
35599     },
35600
35601     /**
35602      * Hides this menu and optionally all parent menus
35603      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35604      */
35605     hide : function(deep){
35606         if(this.el && this.isVisible()){
35607             this.fireEvent("beforehide", this);
35608             if(this.activeItem){
35609                 this.activeItem.deactivate();
35610                 this.activeItem = null;
35611             }
35612             this.el.hide();
35613             this.hidden = true;
35614             this.fireEvent("hide", this);
35615         }
35616         if(deep === true && this.parentMenu){
35617             this.parentMenu.hide(true);
35618         }
35619     },
35620
35621     /**
35622      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35623      * Any of the following are valid:
35624      * <ul>
35625      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35626      * <li>An HTMLElement object which will be converted to a menu item</li>
35627      * <li>A menu item config object that will be created as a new menu item</li>
35628      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35629      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35630      * </ul>
35631      * Usage:
35632      * <pre><code>
35633 // Create the menu
35634 var menu = new Roo.menu.Menu();
35635
35636 // Create a menu item to add by reference
35637 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35638
35639 // Add a bunch of items at once using different methods.
35640 // Only the last item added will be returned.
35641 var item = menu.add(
35642     menuItem,                // add existing item by ref
35643     'Dynamic Item',          // new TextItem
35644     '-',                     // new separator
35645     { text: 'Config Item' }  // new item by config
35646 );
35647 </code></pre>
35648      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35649      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35650      */
35651     add : function(){
35652         var a = arguments, l = a.length, item;
35653         for(var i = 0; i < l; i++){
35654             var el = a[i];
35655             if ((typeof(el) == "object") && el.xtype && el.xns) {
35656                 el = Roo.factory(el, Roo.menu);
35657             }
35658             
35659             if(el.render){ // some kind of Item
35660                 item = this.addItem(el);
35661             }else if(typeof el == "string"){ // string
35662                 if(el == "separator" || el == "-"){
35663                     item = this.addSeparator();
35664                 }else{
35665                     item = this.addText(el);
35666                 }
35667             }else if(el.tagName || el.el){ // element
35668                 item = this.addElement(el);
35669             }else if(typeof el == "object"){ // must be menu item config?
35670                 item = this.addMenuItem(el);
35671             }
35672         }
35673         return item;
35674     },
35675
35676     /**
35677      * Returns this menu's underlying {@link Roo.Element} object
35678      * @return {Roo.Element} The element
35679      */
35680     getEl : function(){
35681         if(!this.el){
35682             this.render();
35683         }
35684         return this.el;
35685     },
35686
35687     /**
35688      * Adds a separator bar to the menu
35689      * @return {Roo.menu.Item} The menu item that was added
35690      */
35691     addSeparator : function(){
35692         return this.addItem(new Roo.menu.Separator());
35693     },
35694
35695     /**
35696      * Adds an {@link Roo.Element} object to the menu
35697      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35698      * @return {Roo.menu.Item} The menu item that was added
35699      */
35700     addElement : function(el){
35701         return this.addItem(new Roo.menu.BaseItem(el));
35702     },
35703
35704     /**
35705      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35706      * @param {Roo.menu.Item} item The menu item to add
35707      * @return {Roo.menu.Item} The menu item that was added
35708      */
35709     addItem : function(item){
35710         this.items.add(item);
35711         if(this.ul){
35712             var li = document.createElement("li");
35713             li.className = "x-menu-list-item";
35714             this.ul.dom.appendChild(li);
35715             item.render(li, this);
35716             this.delayAutoWidth();
35717         }
35718         return item;
35719     },
35720
35721     /**
35722      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35723      * @param {Object} config A MenuItem config object
35724      * @return {Roo.menu.Item} The menu item that was added
35725      */
35726     addMenuItem : function(config){
35727         if(!(config instanceof Roo.menu.Item)){
35728             if(typeof config.checked == "boolean"){ // must be check menu item config?
35729                 config = new Roo.menu.CheckItem(config);
35730             }else{
35731                 config = new Roo.menu.Item(config);
35732             }
35733         }
35734         return this.addItem(config);
35735     },
35736
35737     /**
35738      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35739      * @param {String} text The text to display in the menu item
35740      * @return {Roo.menu.Item} The menu item that was added
35741      */
35742     addText : function(text){
35743         return this.addItem(new Roo.menu.TextItem({ text : text }));
35744     },
35745
35746     /**
35747      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35748      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35749      * @param {Roo.menu.Item} item The menu item to add
35750      * @return {Roo.menu.Item} The menu item that was added
35751      */
35752     insert : function(index, item){
35753         this.items.insert(index, item);
35754         if(this.ul){
35755             var li = document.createElement("li");
35756             li.className = "x-menu-list-item";
35757             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35758             item.render(li, this);
35759             this.delayAutoWidth();
35760         }
35761         return item;
35762     },
35763
35764     /**
35765      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35766      * @param {Roo.menu.Item} item The menu item to remove
35767      */
35768     remove : function(item){
35769         this.items.removeKey(item.id);
35770         item.destroy();
35771     },
35772
35773     /**
35774      * Removes and destroys all items in the menu
35775      */
35776     removeAll : function(){
35777         var f;
35778         while(f = this.items.first()){
35779             this.remove(f);
35780         }
35781     }
35782 });
35783
35784 // MenuNav is a private utility class used internally by the Menu
35785 Roo.menu.MenuNav = function(menu){
35786     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35787     this.scope = this.menu = menu;
35788 };
35789
35790 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35791     doRelay : function(e, h){
35792         var k = e.getKey();
35793         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35794             this.menu.tryActivate(0, 1);
35795             return false;
35796         }
35797         return h.call(this.scope || this, e, this.menu);
35798     },
35799
35800     up : function(e, m){
35801         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35802             m.tryActivate(m.items.length-1, -1);
35803         }
35804     },
35805
35806     down : function(e, m){
35807         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35808             m.tryActivate(0, 1);
35809         }
35810     },
35811
35812     right : function(e, m){
35813         if(m.activeItem){
35814             m.activeItem.expandMenu(true);
35815         }
35816     },
35817
35818     left : function(e, m){
35819         m.hide();
35820         if(m.parentMenu && m.parentMenu.activeItem){
35821             m.parentMenu.activeItem.activate();
35822         }
35823     },
35824
35825     enter : function(e, m){
35826         if(m.activeItem){
35827             e.stopPropagation();
35828             m.activeItem.onClick(e);
35829             m.fireEvent("click", this, m.activeItem);
35830             return true;
35831         }
35832     }
35833 });/*
35834  * Based on:
35835  * Ext JS Library 1.1.1
35836  * Copyright(c) 2006-2007, Ext JS, LLC.
35837  *
35838  * Originally Released Under LGPL - original licence link has changed is not relivant.
35839  *
35840  * Fork - LGPL
35841  * <script type="text/javascript">
35842  */
35843  
35844 /**
35845  * @class Roo.menu.MenuMgr
35846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35847  * @singleton
35848  */
35849 Roo.menu.MenuMgr = function(){
35850    var menus, active, groups = {}, attached = false, lastShow = new Date();
35851
35852    // private - called when first menu is created
35853    function init(){
35854        menus = {};
35855        active = new Roo.util.MixedCollection();
35856        Roo.get(document).addKeyListener(27, function(){
35857            if(active.length > 0){
35858                hideAll();
35859            }
35860        });
35861    }
35862
35863    // private
35864    function hideAll(){
35865        if(active && active.length > 0){
35866            var c = active.clone();
35867            c.each(function(m){
35868                m.hide();
35869            });
35870        }
35871    }
35872
35873    // private
35874    function onHide(m){
35875        active.remove(m);
35876        if(active.length < 1){
35877            Roo.get(document).un("mousedown", onMouseDown);
35878            attached = false;
35879        }
35880    }
35881
35882    // private
35883    function onShow(m){
35884        var last = active.last();
35885        lastShow = new Date();
35886        active.add(m);
35887        if(!attached){
35888            Roo.get(document).on("mousedown", onMouseDown);
35889            attached = true;
35890        }
35891        if(m.parentMenu){
35892           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35893           m.parentMenu.activeChild = m;
35894        }else if(last && last.isVisible()){
35895           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35896        }
35897    }
35898
35899    // private
35900    function onBeforeHide(m){
35901        if(m.activeChild){
35902            m.activeChild.hide();
35903        }
35904        if(m.autoHideTimer){
35905            clearTimeout(m.autoHideTimer);
35906            delete m.autoHideTimer;
35907        }
35908    }
35909
35910    // private
35911    function onBeforeShow(m){
35912        var pm = m.parentMenu;
35913        if(!pm && !m.allowOtherMenus){
35914            hideAll();
35915        }else if(pm && pm.activeChild && active != m){
35916            pm.activeChild.hide();
35917        }
35918    }
35919
35920    // private
35921    function onMouseDown(e){
35922        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35923            hideAll();
35924        }
35925    }
35926
35927    // private
35928    function onBeforeCheck(mi, state){
35929        if(state){
35930            var g = groups[mi.group];
35931            for(var i = 0, l = g.length; i < l; i++){
35932                if(g[i] != mi){
35933                    g[i].setChecked(false);
35934                }
35935            }
35936        }
35937    }
35938
35939    return {
35940
35941        /**
35942         * Hides all menus that are currently visible
35943         */
35944        hideAll : function(){
35945             hideAll();  
35946        },
35947
35948        // private
35949        register : function(menu){
35950            if(!menus){
35951                init();
35952            }
35953            menus[menu.id] = menu;
35954            menu.on("beforehide", onBeforeHide);
35955            menu.on("hide", onHide);
35956            menu.on("beforeshow", onBeforeShow);
35957            menu.on("show", onShow);
35958            var g = menu.group;
35959            if(g && menu.events["checkchange"]){
35960                if(!groups[g]){
35961                    groups[g] = [];
35962                }
35963                groups[g].push(menu);
35964                menu.on("checkchange", onCheck);
35965            }
35966        },
35967
35968         /**
35969          * Returns a {@link Roo.menu.Menu} object
35970          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35971          * be used to generate and return a new Menu instance.
35972          */
35973        get : function(menu){
35974            if(typeof menu == "string"){ // menu id
35975                return menus[menu];
35976            }else if(menu.events){  // menu instance
35977                return menu;
35978            }else if(typeof menu.length == 'number'){ // array of menu items?
35979                return new Roo.menu.Menu({items:menu});
35980            }else{ // otherwise, must be a config
35981                return new Roo.menu.Menu(menu);
35982            }
35983        },
35984
35985        // private
35986        unregister : function(menu){
35987            delete menus[menu.id];
35988            menu.un("beforehide", onBeforeHide);
35989            menu.un("hide", onHide);
35990            menu.un("beforeshow", onBeforeShow);
35991            menu.un("show", onShow);
35992            var g = menu.group;
35993            if(g && menu.events["checkchange"]){
35994                groups[g].remove(menu);
35995                menu.un("checkchange", onCheck);
35996            }
35997        },
35998
35999        // private
36000        registerCheckable : function(menuItem){
36001            var g = menuItem.group;
36002            if(g){
36003                if(!groups[g]){
36004                    groups[g] = [];
36005                }
36006                groups[g].push(menuItem);
36007                menuItem.on("beforecheckchange", onBeforeCheck);
36008            }
36009        },
36010
36011        // private
36012        unregisterCheckable : function(menuItem){
36013            var g = menuItem.group;
36014            if(g){
36015                groups[g].remove(menuItem);
36016                menuItem.un("beforecheckchange", onBeforeCheck);
36017            }
36018        }
36019    };
36020 }();/*
36021  * Based on:
36022  * Ext JS Library 1.1.1
36023  * Copyright(c) 2006-2007, Ext JS, LLC.
36024  *
36025  * Originally Released Under LGPL - original licence link has changed is not relivant.
36026  *
36027  * Fork - LGPL
36028  * <script type="text/javascript">
36029  */
36030  
36031
36032 /**
36033  * @class Roo.menu.BaseItem
36034  * @extends Roo.Component
36035  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36036  * management and base configuration options shared by all menu components.
36037  * @constructor
36038  * Creates a new BaseItem
36039  * @param {Object} config Configuration options
36040  */
36041 Roo.menu.BaseItem = function(config){
36042     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36043
36044     this.addEvents({
36045         /**
36046          * @event click
36047          * Fires when this item is clicked
36048          * @param {Roo.menu.BaseItem} this
36049          * @param {Roo.EventObject} e
36050          */
36051         click: true,
36052         /**
36053          * @event activate
36054          * Fires when this item is activated
36055          * @param {Roo.menu.BaseItem} this
36056          */
36057         activate : true,
36058         /**
36059          * @event deactivate
36060          * Fires when this item is deactivated
36061          * @param {Roo.menu.BaseItem} this
36062          */
36063         deactivate : true
36064     });
36065
36066     if(this.handler){
36067         this.on("click", this.handler, this.scope, true);
36068     }
36069 };
36070
36071 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36072     /**
36073      * @cfg {Function} handler
36074      * A function that will handle the click event of this menu item (defaults to undefined)
36075      */
36076     /**
36077      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36078      */
36079     canActivate : false,
36080     
36081      /**
36082      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36083      */
36084     hidden: false,
36085     
36086     /**
36087      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36088      */
36089     activeClass : "x-menu-item-active",
36090     /**
36091      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36092      */
36093     hideOnClick : true,
36094     /**
36095      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36096      */
36097     hideDelay : 100,
36098
36099     // private
36100     ctype: "Roo.menu.BaseItem",
36101
36102     // private
36103     actionMode : "container",
36104
36105     // private
36106     render : function(container, parentMenu){
36107         this.parentMenu = parentMenu;
36108         Roo.menu.BaseItem.superclass.render.call(this, container);
36109         this.container.menuItemId = this.id;
36110     },
36111
36112     // private
36113     onRender : function(container, position){
36114         this.el = Roo.get(this.el);
36115         container.dom.appendChild(this.el.dom);
36116     },
36117
36118     // private
36119     onClick : function(e){
36120         if(!this.disabled && this.fireEvent("click", this, e) !== false
36121                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36122             this.handleClick(e);
36123         }else{
36124             e.stopEvent();
36125         }
36126     },
36127
36128     // private
36129     activate : function(){
36130         if(this.disabled){
36131             return false;
36132         }
36133         var li = this.container;
36134         li.addClass(this.activeClass);
36135         this.region = li.getRegion().adjust(2, 2, -2, -2);
36136         this.fireEvent("activate", this);
36137         return true;
36138     },
36139
36140     // private
36141     deactivate : function(){
36142         this.container.removeClass(this.activeClass);
36143         this.fireEvent("deactivate", this);
36144     },
36145
36146     // private
36147     shouldDeactivate : function(e){
36148         return !this.region || !this.region.contains(e.getPoint());
36149     },
36150
36151     // private
36152     handleClick : function(e){
36153         if(this.hideOnClick){
36154             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36155         }
36156     },
36157
36158     // private
36159     expandMenu : function(autoActivate){
36160         // do nothing
36161     },
36162
36163     // private
36164     hideMenu : function(){
36165         // do nothing
36166     }
36167 });/*
36168  * Based on:
36169  * Ext JS Library 1.1.1
36170  * Copyright(c) 2006-2007, Ext JS, LLC.
36171  *
36172  * Originally Released Under LGPL - original licence link has changed is not relivant.
36173  *
36174  * Fork - LGPL
36175  * <script type="text/javascript">
36176  */
36177  
36178 /**
36179  * @class Roo.menu.Adapter
36180  * @extends Roo.menu.BaseItem
36181  * 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.
36182  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36183  * @constructor
36184  * Creates a new Adapter
36185  * @param {Object} config Configuration options
36186  */
36187 Roo.menu.Adapter = function(component, config){
36188     Roo.menu.Adapter.superclass.constructor.call(this, config);
36189     this.component = component;
36190 };
36191 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36192     // private
36193     canActivate : true,
36194
36195     // private
36196     onRender : function(container, position){
36197         this.component.render(container);
36198         this.el = this.component.getEl();
36199     },
36200
36201     // private
36202     activate : function(){
36203         if(this.disabled){
36204             return false;
36205         }
36206         this.component.focus();
36207         this.fireEvent("activate", this);
36208         return true;
36209     },
36210
36211     // private
36212     deactivate : function(){
36213         this.fireEvent("deactivate", this);
36214     },
36215
36216     // private
36217     disable : function(){
36218         this.component.disable();
36219         Roo.menu.Adapter.superclass.disable.call(this);
36220     },
36221
36222     // private
36223     enable : function(){
36224         this.component.enable();
36225         Roo.menu.Adapter.superclass.enable.call(this);
36226     }
36227 });/*
36228  * Based on:
36229  * Ext JS Library 1.1.1
36230  * Copyright(c) 2006-2007, Ext JS, LLC.
36231  *
36232  * Originally Released Under LGPL - original licence link has changed is not relivant.
36233  *
36234  * Fork - LGPL
36235  * <script type="text/javascript">
36236  */
36237
36238 /**
36239  * @class Roo.menu.TextItem
36240  * @extends Roo.menu.BaseItem
36241  * Adds a static text string to a menu, usually used as either a heading or group separator.
36242  * Note: old style constructor with text is still supported.
36243  * 
36244  * @constructor
36245  * Creates a new TextItem
36246  * @param {Object} cfg Configuration
36247  */
36248 Roo.menu.TextItem = function(cfg){
36249     if (typeof(cfg) == 'string') {
36250         this.text = cfg;
36251     } else {
36252         Roo.apply(this,cfg);
36253     }
36254     
36255     Roo.menu.TextItem.superclass.constructor.call(this);
36256 };
36257
36258 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36259     /**
36260      * @cfg {Boolean} text Text to show on item.
36261      */
36262     text : '',
36263     
36264     /**
36265      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36266      */
36267     hideOnClick : false,
36268     /**
36269      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36270      */
36271     itemCls : "x-menu-text",
36272
36273     // private
36274     onRender : function(){
36275         var s = document.createElement("span");
36276         s.className = this.itemCls;
36277         s.innerHTML = this.text;
36278         this.el = s;
36279         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36280     }
36281 });/*
36282  * Based on:
36283  * Ext JS Library 1.1.1
36284  * Copyright(c) 2006-2007, Ext JS, LLC.
36285  *
36286  * Originally Released Under LGPL - original licence link has changed is not relivant.
36287  *
36288  * Fork - LGPL
36289  * <script type="text/javascript">
36290  */
36291
36292 /**
36293  * @class Roo.menu.Separator
36294  * @extends Roo.menu.BaseItem
36295  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36296  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36297  * @constructor
36298  * @param {Object} config Configuration options
36299  */
36300 Roo.menu.Separator = function(config){
36301     Roo.menu.Separator.superclass.constructor.call(this, config);
36302 };
36303
36304 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36305     /**
36306      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36307      */
36308     itemCls : "x-menu-sep",
36309     /**
36310      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36311      */
36312     hideOnClick : false,
36313
36314     // private
36315     onRender : function(li){
36316         var s = document.createElement("span");
36317         s.className = this.itemCls;
36318         s.innerHTML = "&#160;";
36319         this.el = s;
36320         li.addClass("x-menu-sep-li");
36321         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36322     }
36323 });/*
36324  * Based on:
36325  * Ext JS Library 1.1.1
36326  * Copyright(c) 2006-2007, Ext JS, LLC.
36327  *
36328  * Originally Released Under LGPL - original licence link has changed is not relivant.
36329  *
36330  * Fork - LGPL
36331  * <script type="text/javascript">
36332  */
36333 /**
36334  * @class Roo.menu.Item
36335  * @extends Roo.menu.BaseItem
36336  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36337  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36338  * activation and click handling.
36339  * @constructor
36340  * Creates a new Item
36341  * @param {Object} config Configuration options
36342  */
36343 Roo.menu.Item = function(config){
36344     Roo.menu.Item.superclass.constructor.call(this, config);
36345     if(this.menu){
36346         this.menu = Roo.menu.MenuMgr.get(this.menu);
36347     }
36348 };
36349 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36350     
36351     /**
36352      * @cfg {String} text
36353      * The text to show on the menu item.
36354      */
36355     text: '',
36356      /**
36357      * @cfg {String} HTML to render in menu
36358      * The text to show on the menu item (HTML version).
36359      */
36360     html: '',
36361     /**
36362      * @cfg {String} icon
36363      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36364      */
36365     icon: undefined,
36366     /**
36367      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36368      */
36369     itemCls : "x-menu-item",
36370     /**
36371      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36372      */
36373     canActivate : true,
36374     /**
36375      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36376      */
36377     showDelay: 200,
36378     // doc'd in BaseItem
36379     hideDelay: 200,
36380
36381     // private
36382     ctype: "Roo.menu.Item",
36383     
36384     // private
36385     onRender : function(container, position){
36386         var el = document.createElement("a");
36387         el.hideFocus = true;
36388         el.unselectable = "on";
36389         el.href = this.href || "#";
36390         if(this.hrefTarget){
36391             el.target = this.hrefTarget;
36392         }
36393         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36394         
36395         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36396         
36397         el.innerHTML = String.format(
36398                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36399                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36400         this.el = el;
36401         Roo.menu.Item.superclass.onRender.call(this, container, position);
36402     },
36403
36404     /**
36405      * Sets the text to display in this menu item
36406      * @param {String} text The text to display
36407      * @param {Boolean} isHTML true to indicate text is pure html.
36408      */
36409     setText : function(text, isHTML){
36410         if (isHTML) {
36411             this.html = text;
36412         } else {
36413             this.text = text;
36414             this.html = '';
36415         }
36416         if(this.rendered){
36417             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36418      
36419             this.el.update(String.format(
36420                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36421                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36422             this.parentMenu.autoWidth();
36423         }
36424     },
36425
36426     // private
36427     handleClick : function(e){
36428         if(!this.href){ // if no link defined, stop the event automatically
36429             e.stopEvent();
36430         }
36431         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36432     },
36433
36434     // private
36435     activate : function(autoExpand){
36436         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36437             this.focus();
36438             if(autoExpand){
36439                 this.expandMenu();
36440             }
36441         }
36442         return true;
36443     },
36444
36445     // private
36446     shouldDeactivate : function(e){
36447         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36448             if(this.menu && this.menu.isVisible()){
36449                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36450             }
36451             return true;
36452         }
36453         return false;
36454     },
36455
36456     // private
36457     deactivate : function(){
36458         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36459         this.hideMenu();
36460     },
36461
36462     // private
36463     expandMenu : function(autoActivate){
36464         if(!this.disabled && this.menu){
36465             clearTimeout(this.hideTimer);
36466             delete this.hideTimer;
36467             if(!this.menu.isVisible() && !this.showTimer){
36468                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36469             }else if (this.menu.isVisible() && autoActivate){
36470                 this.menu.tryActivate(0, 1);
36471             }
36472         }
36473     },
36474
36475     // private
36476     deferExpand : function(autoActivate){
36477         delete this.showTimer;
36478         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36479         if(autoActivate){
36480             this.menu.tryActivate(0, 1);
36481         }
36482     },
36483
36484     // private
36485     hideMenu : function(){
36486         clearTimeout(this.showTimer);
36487         delete this.showTimer;
36488         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36489             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36490         }
36491     },
36492
36493     // private
36494     deferHide : function(){
36495         delete this.hideTimer;
36496         this.menu.hide();
36497     }
36498 });/*
36499  * Based on:
36500  * Ext JS Library 1.1.1
36501  * Copyright(c) 2006-2007, Ext JS, LLC.
36502  *
36503  * Originally Released Under LGPL - original licence link has changed is not relivant.
36504  *
36505  * Fork - LGPL
36506  * <script type="text/javascript">
36507  */
36508  
36509 /**
36510  * @class Roo.menu.CheckItem
36511  * @extends Roo.menu.Item
36512  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36513  * @constructor
36514  * Creates a new CheckItem
36515  * @param {Object} config Configuration options
36516  */
36517 Roo.menu.CheckItem = function(config){
36518     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36519     this.addEvents({
36520         /**
36521          * @event beforecheckchange
36522          * Fires before the checked value is set, providing an opportunity to cancel if needed
36523          * @param {Roo.menu.CheckItem} this
36524          * @param {Boolean} checked The new checked value that will be set
36525          */
36526         "beforecheckchange" : true,
36527         /**
36528          * @event checkchange
36529          * Fires after the checked value has been set
36530          * @param {Roo.menu.CheckItem} this
36531          * @param {Boolean} checked The checked value that was set
36532          */
36533         "checkchange" : true
36534     });
36535     if(this.checkHandler){
36536         this.on('checkchange', this.checkHandler, this.scope);
36537     }
36538 };
36539 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36540     /**
36541      * @cfg {String} group
36542      * All check items with the same group name will automatically be grouped into a single-select
36543      * radio button group (defaults to '')
36544      */
36545     /**
36546      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36547      */
36548     itemCls : "x-menu-item x-menu-check-item",
36549     /**
36550      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36551      */
36552     groupClass : "x-menu-group-item",
36553
36554     /**
36555      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36556      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36557      * initialized with checked = true will be rendered as checked.
36558      */
36559     checked: false,
36560
36561     // private
36562     ctype: "Roo.menu.CheckItem",
36563
36564     // private
36565     onRender : function(c){
36566         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36567         if(this.group){
36568             this.el.addClass(this.groupClass);
36569         }
36570         Roo.menu.MenuMgr.registerCheckable(this);
36571         if(this.checked){
36572             this.checked = false;
36573             this.setChecked(true, true);
36574         }
36575     },
36576
36577     // private
36578     destroy : function(){
36579         if(this.rendered){
36580             Roo.menu.MenuMgr.unregisterCheckable(this);
36581         }
36582         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36583     },
36584
36585     /**
36586      * Set the checked state of this item
36587      * @param {Boolean} checked The new checked value
36588      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36589      */
36590     setChecked : function(state, suppressEvent){
36591         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36592             if(this.container){
36593                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36594             }
36595             this.checked = state;
36596             if(suppressEvent !== true){
36597                 this.fireEvent("checkchange", this, state);
36598             }
36599         }
36600     },
36601
36602     // private
36603     handleClick : function(e){
36604        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36605            this.setChecked(!this.checked);
36606        }
36607        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36608     }
36609 });/*
36610  * Based on:
36611  * Ext JS Library 1.1.1
36612  * Copyright(c) 2006-2007, Ext JS, LLC.
36613  *
36614  * Originally Released Under LGPL - original licence link has changed is not relivant.
36615  *
36616  * Fork - LGPL
36617  * <script type="text/javascript">
36618  */
36619  
36620 /**
36621  * @class Roo.menu.DateItem
36622  * @extends Roo.menu.Adapter
36623  * A menu item that wraps the {@link Roo.DatPicker} component.
36624  * @constructor
36625  * Creates a new DateItem
36626  * @param {Object} config Configuration options
36627  */
36628 Roo.menu.DateItem = function(config){
36629     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36630     /** The Roo.DatePicker object @type Roo.DatePicker */
36631     this.picker = this.component;
36632     this.addEvents({select: true});
36633     
36634     this.picker.on("render", function(picker){
36635         picker.getEl().swallowEvent("click");
36636         picker.container.addClass("x-menu-date-item");
36637     });
36638
36639     this.picker.on("select", this.onSelect, this);
36640 };
36641
36642 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36643     // private
36644     onSelect : function(picker, date){
36645         this.fireEvent("select", this, date, picker);
36646         Roo.menu.DateItem.superclass.handleClick.call(this);
36647     }
36648 });/*
36649  * Based on:
36650  * Ext JS Library 1.1.1
36651  * Copyright(c) 2006-2007, Ext JS, LLC.
36652  *
36653  * Originally Released Under LGPL - original licence link has changed is not relivant.
36654  *
36655  * Fork - LGPL
36656  * <script type="text/javascript">
36657  */
36658  
36659 /**
36660  * @class Roo.menu.ColorItem
36661  * @extends Roo.menu.Adapter
36662  * A menu item that wraps the {@link Roo.ColorPalette} component.
36663  * @constructor
36664  * Creates a new ColorItem
36665  * @param {Object} config Configuration options
36666  */
36667 Roo.menu.ColorItem = function(config){
36668     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36669     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36670     this.palette = this.component;
36671     this.relayEvents(this.palette, ["select"]);
36672     if(this.selectHandler){
36673         this.on('select', this.selectHandler, this.scope);
36674     }
36675 };
36676 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36677  * Based on:
36678  * Ext JS Library 1.1.1
36679  * Copyright(c) 2006-2007, Ext JS, LLC.
36680  *
36681  * Originally Released Under LGPL - original licence link has changed is not relivant.
36682  *
36683  * Fork - LGPL
36684  * <script type="text/javascript">
36685  */
36686  
36687
36688 /**
36689  * @class Roo.menu.DateMenu
36690  * @extends Roo.menu.Menu
36691  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36692  * @constructor
36693  * Creates a new DateMenu
36694  * @param {Object} config Configuration options
36695  */
36696 Roo.menu.DateMenu = function(config){
36697     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36698     this.plain = true;
36699     var di = new Roo.menu.DateItem(config);
36700     this.add(di);
36701     /**
36702      * The {@link Roo.DatePicker} instance for this DateMenu
36703      * @type DatePicker
36704      */
36705     this.picker = di.picker;
36706     /**
36707      * @event select
36708      * @param {DatePicker} picker
36709      * @param {Date} date
36710      */
36711     this.relayEvents(di, ["select"]);
36712     this.on('beforeshow', function(){
36713         if(this.picker){
36714             this.picker.hideMonthPicker(false);
36715         }
36716     }, this);
36717 };
36718 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36719     cls:'x-date-menu'
36720 });/*
36721  * Based on:
36722  * Ext JS Library 1.1.1
36723  * Copyright(c) 2006-2007, Ext JS, LLC.
36724  *
36725  * Originally Released Under LGPL - original licence link has changed is not relivant.
36726  *
36727  * Fork - LGPL
36728  * <script type="text/javascript">
36729  */
36730  
36731
36732 /**
36733  * @class Roo.menu.ColorMenu
36734  * @extends Roo.menu.Menu
36735  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36736  * @constructor
36737  * Creates a new ColorMenu
36738  * @param {Object} config Configuration options
36739  */
36740 Roo.menu.ColorMenu = function(config){
36741     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36742     this.plain = true;
36743     var ci = new Roo.menu.ColorItem(config);
36744     this.add(ci);
36745     /**
36746      * The {@link Roo.ColorPalette} instance for this ColorMenu
36747      * @type ColorPalette
36748      */
36749     this.palette = ci.palette;
36750     /**
36751      * @event select
36752      * @param {ColorPalette} palette
36753      * @param {String} color
36754      */
36755     this.relayEvents(ci, ["select"]);
36756 };
36757 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36758  * Based on:
36759  * Ext JS Library 1.1.1
36760  * Copyright(c) 2006-2007, Ext JS, LLC.
36761  *
36762  * Originally Released Under LGPL - original licence link has changed is not relivant.
36763  *
36764  * Fork - LGPL
36765  * <script type="text/javascript">
36766  */
36767  
36768 /**
36769  * @class Roo.form.Field
36770  * @extends Roo.BoxComponent
36771  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36772  * @constructor
36773  * Creates a new Field
36774  * @param {Object} config Configuration options
36775  */
36776 Roo.form.Field = function(config){
36777     Roo.form.Field.superclass.constructor.call(this, config);
36778 };
36779
36780 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36781     /**
36782      * @cfg {String} fieldLabel Label to use when rendering a form.
36783      */
36784        /**
36785      * @cfg {String} qtip Mouse over tip
36786      */
36787      
36788     /**
36789      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36790      */
36791     invalidClass : "x-form-invalid",
36792     /**
36793      * @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")
36794      */
36795     invalidText : "The value in this field is invalid",
36796     /**
36797      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36798      */
36799     focusClass : "x-form-focus",
36800     /**
36801      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36802       automatic validation (defaults to "keyup").
36803      */
36804     validationEvent : "keyup",
36805     /**
36806      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36807      */
36808     validateOnBlur : true,
36809     /**
36810      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36811      */
36812     validationDelay : 250,
36813     /**
36814      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36815      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36816      */
36817     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36818     /**
36819      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36820      */
36821     fieldClass : "x-form-field",
36822     /**
36823      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36824      *<pre>
36825 Value         Description
36826 -----------   ----------------------------------------------------------------------
36827 qtip          Display a quick tip when the user hovers over the field
36828 title         Display a default browser title attribute popup
36829 under         Add a block div beneath the field containing the error text
36830 side          Add an error icon to the right of the field with a popup on hover
36831 [element id]  Add the error text directly to the innerHTML of the specified element
36832 </pre>
36833      */
36834     msgTarget : 'qtip',
36835     /**
36836      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36837      */
36838     msgFx : 'normal',
36839
36840     /**
36841      * @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.
36842      */
36843     readOnly : false,
36844
36845     /**
36846      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36847      */
36848     disabled : false,
36849
36850     /**
36851      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36852      */
36853     inputType : undefined,
36854     
36855     /**
36856      * @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).
36857          */
36858         tabIndex : undefined,
36859         
36860     // private
36861     isFormField : true,
36862
36863     // private
36864     hasFocus : false,
36865     /**
36866      * @property {Roo.Element} fieldEl
36867      * Element Containing the rendered Field (with label etc.)
36868      */
36869     /**
36870      * @cfg {Mixed} value A value to initialize this field with.
36871      */
36872     value : undefined,
36873
36874     /**
36875      * @cfg {String} name The field's HTML name attribute.
36876      */
36877     /**
36878      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36879      */
36880
36881         // private ??
36882         initComponent : function(){
36883         Roo.form.Field.superclass.initComponent.call(this);
36884         this.addEvents({
36885             /**
36886              * @event focus
36887              * Fires when this field receives input focus.
36888              * @param {Roo.form.Field} this
36889              */
36890             focus : true,
36891             /**
36892              * @event blur
36893              * Fires when this field loses input focus.
36894              * @param {Roo.form.Field} this
36895              */
36896             blur : true,
36897             /**
36898              * @event specialkey
36899              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36900              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36901              * @param {Roo.form.Field} this
36902              * @param {Roo.EventObject} e The event object
36903              */
36904             specialkey : true,
36905             /**
36906              * @event change
36907              * Fires just before the field blurs if the field value has changed.
36908              * @param {Roo.form.Field} this
36909              * @param {Mixed} newValue The new value
36910              * @param {Mixed} oldValue The original value
36911              */
36912             change : true,
36913             /**
36914              * @event invalid
36915              * Fires after the field has been marked as invalid.
36916              * @param {Roo.form.Field} this
36917              * @param {String} msg The validation message
36918              */
36919             invalid : true,
36920             /**
36921              * @event valid
36922              * Fires after the field has been validated with no errors.
36923              * @param {Roo.form.Field} this
36924              */
36925             valid : true,
36926              /**
36927              * @event keyup
36928              * Fires after the key up
36929              * @param {Roo.form.Field} this
36930              * @param {Roo.EventObject}  e The event Object
36931              */
36932             keyup : true
36933         });
36934     },
36935
36936     /**
36937      * Returns the name attribute of the field if available
36938      * @return {String} name The field name
36939      */
36940     getName: function(){
36941          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36942     },
36943
36944     // private
36945     onRender : function(ct, position){
36946         Roo.form.Field.superclass.onRender.call(this, ct, position);
36947         if(!this.el){
36948             var cfg = this.getAutoCreate();
36949             if(!cfg.name){
36950                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36951             }
36952             if (!cfg.name.length) {
36953                 delete cfg.name;
36954             }
36955             if(this.inputType){
36956                 cfg.type = this.inputType;
36957             }
36958             this.el = ct.createChild(cfg, position);
36959         }
36960         var type = this.el.dom.type;
36961         if(type){
36962             if(type == 'password'){
36963                 type = 'text';
36964             }
36965             this.el.addClass('x-form-'+type);
36966         }
36967         if(this.readOnly){
36968             this.el.dom.readOnly = true;
36969         }
36970         if(this.tabIndex !== undefined){
36971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36972         }
36973
36974         this.el.addClass([this.fieldClass, this.cls]);
36975         this.initValue();
36976     },
36977
36978     /**
36979      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36980      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36981      * @return {Roo.form.Field} this
36982      */
36983     applyTo : function(target){
36984         this.allowDomMove = false;
36985         this.el = Roo.get(target);
36986         this.render(this.el.dom.parentNode);
36987         return this;
36988     },
36989
36990     // private
36991     initValue : function(){
36992         if(this.value !== undefined){
36993             this.setValue(this.value);
36994         }else if(this.el.dom.value.length > 0){
36995             this.setValue(this.el.dom.value);
36996         }
36997     },
36998
36999     /**
37000      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37001      */
37002     isDirty : function() {
37003         if(this.disabled) {
37004             return false;
37005         }
37006         return String(this.getValue()) !== String(this.originalValue);
37007     },
37008
37009     // private
37010     afterRender : function(){
37011         Roo.form.Field.superclass.afterRender.call(this);
37012         this.initEvents();
37013     },
37014
37015     // private
37016     fireKey : function(e){
37017         //Roo.log('field ' + e.getKey());
37018         if(e.isNavKeyPress()){
37019             this.fireEvent("specialkey", this, e);
37020         }
37021     },
37022
37023     /**
37024      * Resets the current field value to the originally loaded value and clears any validation messages
37025      */
37026     reset : function(){
37027         this.setValue(this.originalValue);
37028         this.clearInvalid();
37029     },
37030
37031     // private
37032     initEvents : function(){
37033         // safari killled keypress - so keydown is now used..
37034         this.el.on("keydown" , this.fireKey,  this);
37035         this.el.on("focus", this.onFocus,  this);
37036         this.el.on("blur", this.onBlur,  this);
37037         this.el.relayEvent('keyup', this);
37038
37039         // reference to original value for reset
37040         this.originalValue = this.getValue();
37041     },
37042
37043     // private
37044     onFocus : function(){
37045         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37046             this.el.addClass(this.focusClass);
37047         }
37048         if(!this.hasFocus){
37049             this.hasFocus = true;
37050             this.startValue = this.getValue();
37051             this.fireEvent("focus", this);
37052         }
37053     },
37054
37055     beforeBlur : Roo.emptyFn,
37056
37057     // private
37058     onBlur : function(){
37059         this.beforeBlur();
37060         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37061             this.el.removeClass(this.focusClass);
37062         }
37063         this.hasFocus = false;
37064         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37065             this.validate();
37066         }
37067         var v = this.getValue();
37068         if(String(v) !== String(this.startValue)){
37069             this.fireEvent('change', this, v, this.startValue);
37070         }
37071         this.fireEvent("blur", this);
37072     },
37073
37074     /**
37075      * Returns whether or not the field value is currently valid
37076      * @param {Boolean} preventMark True to disable marking the field invalid
37077      * @return {Boolean} True if the value is valid, else false
37078      */
37079     isValid : function(preventMark){
37080         if(this.disabled){
37081             return true;
37082         }
37083         var restore = this.preventMark;
37084         this.preventMark = preventMark === true;
37085         var v = this.validateValue(this.processValue(this.getRawValue()));
37086         this.preventMark = restore;
37087         return v;
37088     },
37089
37090     /**
37091      * Validates the field value
37092      * @return {Boolean} True if the value is valid, else false
37093      */
37094     validate : function(){
37095         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37096             this.clearInvalid();
37097             return true;
37098         }
37099         return false;
37100     },
37101
37102     processValue : function(value){
37103         return value;
37104     },
37105
37106     // private
37107     // Subclasses should provide the validation implementation by overriding this
37108     validateValue : function(value){
37109         return true;
37110     },
37111
37112     /**
37113      * Mark this field as invalid
37114      * @param {String} msg The validation message
37115      */
37116     markInvalid : function(msg){
37117         if(!this.rendered || this.preventMark){ // not rendered
37118             return;
37119         }
37120         this.el.addClass(this.invalidClass);
37121         msg = msg || this.invalidText;
37122         switch(this.msgTarget){
37123             case 'qtip':
37124                 this.el.dom.qtip = msg;
37125                 this.el.dom.qclass = 'x-form-invalid-tip';
37126                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37127                     Roo.QuickTips.enable();
37128                 }
37129                 break;
37130             case 'title':
37131                 this.el.dom.title = msg;
37132                 break;
37133             case 'under':
37134                 if(!this.errorEl){
37135                     var elp = this.el.findParent('.x-form-element', 5, true);
37136                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37137                     this.errorEl.setWidth(elp.getWidth(true)-20);
37138                 }
37139                 this.errorEl.update(msg);
37140                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37141                 break;
37142             case 'side':
37143                 if(!this.errorIcon){
37144                     var elp = this.el.findParent('.x-form-element', 5, true);
37145                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37146                 }
37147                 this.alignErrorIcon();
37148                 this.errorIcon.dom.qtip = msg;
37149                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37150                 this.errorIcon.show();
37151                 this.on('resize', this.alignErrorIcon, this);
37152                 break;
37153             default:
37154                 var t = Roo.getDom(this.msgTarget);
37155                 t.innerHTML = msg;
37156                 t.style.display = this.msgDisplay;
37157                 break;
37158         }
37159         this.fireEvent('invalid', this, msg);
37160     },
37161
37162     // private
37163     alignErrorIcon : function(){
37164         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37165     },
37166
37167     /**
37168      * Clear any invalid styles/messages for this field
37169      */
37170     clearInvalid : function(){
37171         if(!this.rendered || this.preventMark){ // not rendered
37172             return;
37173         }
37174         this.el.removeClass(this.invalidClass);
37175         switch(this.msgTarget){
37176             case 'qtip':
37177                 this.el.dom.qtip = '';
37178                 break;
37179             case 'title':
37180                 this.el.dom.title = '';
37181                 break;
37182             case 'under':
37183                 if(this.errorEl){
37184                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37185                 }
37186                 break;
37187             case 'side':
37188                 if(this.errorIcon){
37189                     this.errorIcon.dom.qtip = '';
37190                     this.errorIcon.hide();
37191                     this.un('resize', this.alignErrorIcon, this);
37192                 }
37193                 break;
37194             default:
37195                 var t = Roo.getDom(this.msgTarget);
37196                 t.innerHTML = '';
37197                 t.style.display = 'none';
37198                 break;
37199         }
37200         this.fireEvent('valid', this);
37201     },
37202
37203     /**
37204      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37205      * @return {Mixed} value The field value
37206      */
37207     getRawValue : function(){
37208         var v = this.el.getValue();
37209         
37210         return v;
37211     },
37212
37213     /**
37214      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37215      * @return {Mixed} value The field value
37216      */
37217     getValue : function(){
37218         var v = this.el.getValue();
37219          
37220         return v;
37221     },
37222
37223     /**
37224      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37225      * @param {Mixed} value The value to set
37226      */
37227     setRawValue : function(v){
37228         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37229     },
37230
37231     /**
37232      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37233      * @param {Mixed} value The value to set
37234      */
37235     setValue : function(v){
37236         this.value = v;
37237         if(this.rendered){
37238             this.el.dom.value = (v === null || v === undefined ? '' : v);
37239              this.validate();
37240         }
37241     },
37242
37243     adjustSize : function(w, h){
37244         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37245         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37246         return s;
37247     },
37248
37249     adjustWidth : function(tag, w){
37250         tag = tag.toLowerCase();
37251         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37252             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37253                 if(tag == 'input'){
37254                     return w + 2;
37255                 }
37256                 if(tag == 'textarea'){
37257                     return w-2;
37258                 }
37259             }else if(Roo.isOpera){
37260                 if(tag == 'input'){
37261                     return w + 2;
37262                 }
37263                 if(tag == 'textarea'){
37264                     return w-2;
37265                 }
37266             }
37267         }
37268         return w;
37269     }
37270 });
37271
37272
37273 // anything other than normal should be considered experimental
37274 Roo.form.Field.msgFx = {
37275     normal : {
37276         show: function(msgEl, f){
37277             msgEl.setDisplayed('block');
37278         },
37279
37280         hide : function(msgEl, f){
37281             msgEl.setDisplayed(false).update('');
37282         }
37283     },
37284
37285     slide : {
37286         show: function(msgEl, f){
37287             msgEl.slideIn('t', {stopFx:true});
37288         },
37289
37290         hide : function(msgEl, f){
37291             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37292         }
37293     },
37294
37295     slideRight : {
37296         show: function(msgEl, f){
37297             msgEl.fixDisplay();
37298             msgEl.alignTo(f.el, 'tl-tr');
37299             msgEl.slideIn('l', {stopFx:true});
37300         },
37301
37302         hide : function(msgEl, f){
37303             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37304         }
37305     }
37306 };/*
37307  * Based on:
37308  * Ext JS Library 1.1.1
37309  * Copyright(c) 2006-2007, Ext JS, LLC.
37310  *
37311  * Originally Released Under LGPL - original licence link has changed is not relivant.
37312  *
37313  * Fork - LGPL
37314  * <script type="text/javascript">
37315  */
37316  
37317
37318 /**
37319  * @class Roo.form.TextField
37320  * @extends Roo.form.Field
37321  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37322  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37323  * @constructor
37324  * Creates a new TextField
37325  * @param {Object} config Configuration options
37326  */
37327 Roo.form.TextField = function(config){
37328     Roo.form.TextField.superclass.constructor.call(this, config);
37329     this.addEvents({
37330         /**
37331          * @event autosize
37332          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37333          * according to the default logic, but this event provides a hook for the developer to apply additional
37334          * logic at runtime to resize the field if needed.
37335              * @param {Roo.form.Field} this This text field
37336              * @param {Number} width The new field width
37337              */
37338         autosize : true
37339     });
37340 };
37341
37342 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37343     /**
37344      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37345      */
37346     grow : false,
37347     /**
37348      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37349      */
37350     growMin : 30,
37351     /**
37352      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37353      */
37354     growMax : 800,
37355     /**
37356      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37357      */
37358     vtype : null,
37359     /**
37360      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37361      */
37362     maskRe : null,
37363     /**
37364      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37365      */
37366     disableKeyFilter : false,
37367     /**
37368      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37369      */
37370     allowBlank : true,
37371     /**
37372      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37373      */
37374     minLength : 0,
37375     /**
37376      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37377      */
37378     maxLength : Number.MAX_VALUE,
37379     /**
37380      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37381      */
37382     minLengthText : "The minimum length for this field is {0}",
37383     /**
37384      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37385      */
37386     maxLengthText : "The maximum length for this field is {0}",
37387     /**
37388      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37389      */
37390     selectOnFocus : false,
37391     /**
37392      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37393      */
37394     blankText : "This field is required",
37395     /**
37396      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37397      * If available, this function will be called only after the basic validators all return true, and will be passed the
37398      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37399      */
37400     validator : null,
37401     /**
37402      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37403      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37404      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37405      */
37406     regex : null,
37407     /**
37408      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37409      */
37410     regexText : "",
37411     /**
37412      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37413      */
37414     emptyText : null,
37415    
37416
37417     // private
37418     initEvents : function()
37419     {
37420         if (this.emptyText) {
37421             this.el.attr('placeholder', this.emptyText);
37422         }
37423         
37424         Roo.form.TextField.superclass.initEvents.call(this);
37425         if(this.validationEvent == 'keyup'){
37426             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37427             this.el.on('keyup', this.filterValidation, this);
37428         }
37429         else if(this.validationEvent !== false){
37430             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37431         }
37432         
37433         if(this.selectOnFocus){
37434             this.on("focus", this.preFocus, this);
37435             
37436         }
37437         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37438             this.el.on("keypress", this.filterKeys, this);
37439         }
37440         if(this.grow){
37441             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37442             this.el.on("click", this.autoSize,  this);
37443         }
37444         if(this.el.is('input[type=password]') && Roo.isSafari){
37445             this.el.on('keydown', this.SafariOnKeyDown, this);
37446         }
37447     },
37448
37449     processValue : function(value){
37450         if(this.stripCharsRe){
37451             var newValue = value.replace(this.stripCharsRe, '');
37452             if(newValue !== value){
37453                 this.setRawValue(newValue);
37454                 return newValue;
37455             }
37456         }
37457         return value;
37458     },
37459
37460     filterValidation : function(e){
37461         if(!e.isNavKeyPress()){
37462             this.validationTask.delay(this.validationDelay);
37463         }
37464     },
37465
37466     // private
37467     onKeyUp : function(e){
37468         if(!e.isNavKeyPress()){
37469             this.autoSize();
37470         }
37471     },
37472
37473     /**
37474      * Resets the current field value to the originally-loaded value and clears any validation messages.
37475      *  
37476      */
37477     reset : function(){
37478         Roo.form.TextField.superclass.reset.call(this);
37479        
37480     },
37481
37482     
37483     // private
37484     preFocus : function(){
37485         
37486         if(this.selectOnFocus){
37487             this.el.dom.select();
37488         }
37489     },
37490
37491     
37492     // private
37493     filterKeys : function(e){
37494         var k = e.getKey();
37495         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37496             return;
37497         }
37498         var c = e.getCharCode(), cc = String.fromCharCode(c);
37499         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37500             return;
37501         }
37502         if(!this.maskRe.test(cc)){
37503             e.stopEvent();
37504         }
37505     },
37506
37507     setValue : function(v){
37508         
37509         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37510         
37511         this.autoSize();
37512     },
37513
37514     /**
37515      * Validates a value according to the field's validation rules and marks the field as invalid
37516      * if the validation fails
37517      * @param {Mixed} value The value to validate
37518      * @return {Boolean} True if the value is valid, else false
37519      */
37520     validateValue : function(value){
37521         if(value.length < 1)  { // if it's blank
37522              if(this.allowBlank){
37523                 this.clearInvalid();
37524                 return true;
37525              }else{
37526                 this.markInvalid(this.blankText);
37527                 return false;
37528              }
37529         }
37530         if(value.length < this.minLength){
37531             this.markInvalid(String.format(this.minLengthText, this.minLength));
37532             return false;
37533         }
37534         if(value.length > this.maxLength){
37535             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37536             return false;
37537         }
37538         if(this.vtype){
37539             var vt = Roo.form.VTypes;
37540             if(!vt[this.vtype](value, this)){
37541                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37542                 return false;
37543             }
37544         }
37545         if(typeof this.validator == "function"){
37546             var msg = this.validator(value);
37547             if(msg !== true){
37548                 this.markInvalid(msg);
37549                 return false;
37550             }
37551         }
37552         if(this.regex && !this.regex.test(value)){
37553             this.markInvalid(this.regexText);
37554             return false;
37555         }
37556         return true;
37557     },
37558
37559     /**
37560      * Selects text in this field
37561      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37562      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37563      */
37564     selectText : function(start, end){
37565         var v = this.getRawValue();
37566         if(v.length > 0){
37567             start = start === undefined ? 0 : start;
37568             end = end === undefined ? v.length : end;
37569             var d = this.el.dom;
37570             if(d.setSelectionRange){
37571                 d.setSelectionRange(start, end);
37572             }else if(d.createTextRange){
37573                 var range = d.createTextRange();
37574                 range.moveStart("character", start);
37575                 range.moveEnd("character", v.length-end);
37576                 range.select();
37577             }
37578         }
37579     },
37580
37581     /**
37582      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37583      * This only takes effect if grow = true, and fires the autosize event.
37584      */
37585     autoSize : function(){
37586         if(!this.grow || !this.rendered){
37587             return;
37588         }
37589         if(!this.metrics){
37590             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37591         }
37592         var el = this.el;
37593         var v = el.dom.value;
37594         var d = document.createElement('div');
37595         d.appendChild(document.createTextNode(v));
37596         v = d.innerHTML;
37597         d = null;
37598         v += "&#160;";
37599         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37600         this.el.setWidth(w);
37601         this.fireEvent("autosize", this, w);
37602     },
37603     
37604     // private
37605     SafariOnKeyDown : function(event)
37606     {
37607         // this is a workaround for a password hang bug on chrome/ webkit.
37608         
37609         var isSelectAll = false;
37610         
37611         if(this.el.dom.selectionEnd > 0){
37612             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37613         }
37614         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37615             event.preventDefault();
37616             this.setValue('');
37617             return;
37618         }
37619         
37620         if(isSelectAll){ // backspace and delete key
37621             
37622             event.preventDefault();
37623             // this is very hacky as keydown always get's upper case.
37624             //
37625             var cc = String.fromCharCode(event.getCharCode());
37626             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37627             
37628         }
37629         
37630         
37631     }
37632 });/*
37633  * Based on:
37634  * Ext JS Library 1.1.1
37635  * Copyright(c) 2006-2007, Ext JS, LLC.
37636  *
37637  * Originally Released Under LGPL - original licence link has changed is not relivant.
37638  *
37639  * Fork - LGPL
37640  * <script type="text/javascript">
37641  */
37642  
37643 /**
37644  * @class Roo.form.Hidden
37645  * @extends Roo.form.TextField
37646  * Simple Hidden element used on forms 
37647  * 
37648  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37649  * 
37650  * @constructor
37651  * Creates a new Hidden form element.
37652  * @param {Object} config Configuration options
37653  */
37654
37655
37656
37657 // easy hidden field...
37658 Roo.form.Hidden = function(config){
37659     Roo.form.Hidden.superclass.constructor.call(this, config);
37660 };
37661   
37662 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37663     fieldLabel:      '',
37664     inputType:      'hidden',
37665     width:          50,
37666     allowBlank:     true,
37667     labelSeparator: '',
37668     hidden:         true,
37669     itemCls :       'x-form-item-display-none'
37670
37671
37672 });
37673
37674
37675 /*
37676  * Based on:
37677  * Ext JS Library 1.1.1
37678  * Copyright(c) 2006-2007, Ext JS, LLC.
37679  *
37680  * Originally Released Under LGPL - original licence link has changed is not relivant.
37681  *
37682  * Fork - LGPL
37683  * <script type="text/javascript">
37684  */
37685  
37686 /**
37687  * @class Roo.form.TriggerField
37688  * @extends Roo.form.TextField
37689  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37690  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37691  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37692  * for which you can provide a custom implementation.  For example:
37693  * <pre><code>
37694 var trigger = new Roo.form.TriggerField();
37695 trigger.onTriggerClick = myTriggerFn;
37696 trigger.applyTo('my-field');
37697 </code></pre>
37698  *
37699  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37700  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37701  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37702  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37703  * @constructor
37704  * Create a new TriggerField.
37705  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37706  * to the base TextField)
37707  */
37708 Roo.form.TriggerField = function(config){
37709     this.mimicing = false;
37710     Roo.form.TriggerField.superclass.constructor.call(this, config);
37711 };
37712
37713 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37714     /**
37715      * @cfg {String} triggerClass A CSS class to apply to the trigger
37716      */
37717     /**
37718      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37719      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37720      */
37721     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37722     /**
37723      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37724      */
37725     hideTrigger:false,
37726
37727     /** @cfg {Boolean} grow @hide */
37728     /** @cfg {Number} growMin @hide */
37729     /** @cfg {Number} growMax @hide */
37730
37731     /**
37732      * @hide 
37733      * @method
37734      */
37735     autoSize: Roo.emptyFn,
37736     // private
37737     monitorTab : true,
37738     // private
37739     deferHeight : true,
37740
37741     
37742     actionMode : 'wrap',
37743     // private
37744     onResize : function(w, h){
37745         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37746         if(typeof w == 'number'){
37747             var x = w - this.trigger.getWidth();
37748             this.el.setWidth(this.adjustWidth('input', x));
37749             this.trigger.setStyle('left', x+'px');
37750         }
37751     },
37752
37753     // private
37754     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37755
37756     // private
37757     getResizeEl : function(){
37758         return this.wrap;
37759     },
37760
37761     // private
37762     getPositionEl : function(){
37763         return this.wrap;
37764     },
37765
37766     // private
37767     alignErrorIcon : function(){
37768         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37769     },
37770
37771     // private
37772     onRender : function(ct, position){
37773         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37774         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37775         this.trigger = this.wrap.createChild(this.triggerConfig ||
37776                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37777         if(this.hideTrigger){
37778             this.trigger.setDisplayed(false);
37779         }
37780         this.initTrigger();
37781         if(!this.width){
37782             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37783         }
37784     },
37785
37786     // private
37787     initTrigger : function(){
37788         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37789         this.trigger.addClassOnOver('x-form-trigger-over');
37790         this.trigger.addClassOnClick('x-form-trigger-click');
37791     },
37792
37793     // private
37794     onDestroy : function(){
37795         if(this.trigger){
37796             this.trigger.removeAllListeners();
37797             this.trigger.remove();
37798         }
37799         if(this.wrap){
37800             this.wrap.remove();
37801         }
37802         Roo.form.TriggerField.superclass.onDestroy.call(this);
37803     },
37804
37805     // private
37806     onFocus : function(){
37807         Roo.form.TriggerField.superclass.onFocus.call(this);
37808         if(!this.mimicing){
37809             this.wrap.addClass('x-trigger-wrap-focus');
37810             this.mimicing = true;
37811             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37812             if(this.monitorTab){
37813                 this.el.on("keydown", this.checkTab, this);
37814             }
37815         }
37816     },
37817
37818     // private
37819     checkTab : function(e){
37820         if(e.getKey() == e.TAB){
37821             this.triggerBlur();
37822         }
37823     },
37824
37825     // private
37826     onBlur : function(){
37827         // do nothing
37828     },
37829
37830     // private
37831     mimicBlur : function(e, t){
37832         if(!this.wrap.contains(t) && this.validateBlur()){
37833             this.triggerBlur();
37834         }
37835     },
37836
37837     // private
37838     triggerBlur : function(){
37839         this.mimicing = false;
37840         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37841         if(this.monitorTab){
37842             this.el.un("keydown", this.checkTab, this);
37843         }
37844         this.wrap.removeClass('x-trigger-wrap-focus');
37845         Roo.form.TriggerField.superclass.onBlur.call(this);
37846     },
37847
37848     // private
37849     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37850     validateBlur : function(e, t){
37851         return true;
37852     },
37853
37854     // private
37855     onDisable : function(){
37856         Roo.form.TriggerField.superclass.onDisable.call(this);
37857         if(this.wrap){
37858             this.wrap.addClass('x-item-disabled');
37859         }
37860     },
37861
37862     // private
37863     onEnable : function(){
37864         Roo.form.TriggerField.superclass.onEnable.call(this);
37865         if(this.wrap){
37866             this.wrap.removeClass('x-item-disabled');
37867         }
37868     },
37869
37870     // private
37871     onShow : function(){
37872         var ae = this.getActionEl();
37873         
37874         if(ae){
37875             ae.dom.style.display = '';
37876             ae.dom.style.visibility = 'visible';
37877         }
37878     },
37879
37880     // private
37881     
37882     onHide : function(){
37883         var ae = this.getActionEl();
37884         ae.dom.style.display = 'none';
37885     },
37886
37887     /**
37888      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37889      * by an implementing function.
37890      * @method
37891      * @param {EventObject} e
37892      */
37893     onTriggerClick : Roo.emptyFn
37894 });
37895
37896 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37897 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37898 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37899 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37900     initComponent : function(){
37901         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37902
37903         this.triggerConfig = {
37904             tag:'span', cls:'x-form-twin-triggers', cn:[
37905             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37906             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37907         ]};
37908     },
37909
37910     getTrigger : function(index){
37911         return this.triggers[index];
37912     },
37913
37914     initTrigger : function(){
37915         var ts = this.trigger.select('.x-form-trigger', true);
37916         this.wrap.setStyle('overflow', 'hidden');
37917         var triggerField = this;
37918         ts.each(function(t, all, index){
37919             t.hide = function(){
37920                 var w = triggerField.wrap.getWidth();
37921                 this.dom.style.display = 'none';
37922                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37923             };
37924             t.show = function(){
37925                 var w = triggerField.wrap.getWidth();
37926                 this.dom.style.display = '';
37927                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37928             };
37929             var triggerIndex = 'Trigger'+(index+1);
37930
37931             if(this['hide'+triggerIndex]){
37932                 t.dom.style.display = 'none';
37933             }
37934             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37935             t.addClassOnOver('x-form-trigger-over');
37936             t.addClassOnClick('x-form-trigger-click');
37937         }, this);
37938         this.triggers = ts.elements;
37939     },
37940
37941     onTrigger1Click : Roo.emptyFn,
37942     onTrigger2Click : Roo.emptyFn
37943 });/*
37944  * Based on:
37945  * Ext JS Library 1.1.1
37946  * Copyright(c) 2006-2007, Ext JS, LLC.
37947  *
37948  * Originally Released Under LGPL - original licence link has changed is not relivant.
37949  *
37950  * Fork - LGPL
37951  * <script type="text/javascript">
37952  */
37953  
37954 /**
37955  * @class Roo.form.TextArea
37956  * @extends Roo.form.TextField
37957  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37958  * support for auto-sizing.
37959  * @constructor
37960  * Creates a new TextArea
37961  * @param {Object} config Configuration options
37962  */
37963 Roo.form.TextArea = function(config){
37964     Roo.form.TextArea.superclass.constructor.call(this, config);
37965     // these are provided exchanges for backwards compat
37966     // minHeight/maxHeight were replaced by growMin/growMax to be
37967     // compatible with TextField growing config values
37968     if(this.minHeight !== undefined){
37969         this.growMin = this.minHeight;
37970     }
37971     if(this.maxHeight !== undefined){
37972         this.growMax = this.maxHeight;
37973     }
37974 };
37975
37976 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37977     /**
37978      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37979      */
37980     growMin : 60,
37981     /**
37982      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37983      */
37984     growMax: 1000,
37985     /**
37986      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37987      * in the field (equivalent to setting overflow: hidden, defaults to false)
37988      */
37989     preventScrollbars: false,
37990     /**
37991      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37992      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37993      */
37994
37995     // private
37996     onRender : function(ct, position){
37997         if(!this.el){
37998             this.defaultAutoCreate = {
37999                 tag: "textarea",
38000                 style:"width:300px;height:60px;",
38001                 autocomplete: "off"
38002             };
38003         }
38004         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38005         if(this.grow){
38006             this.textSizeEl = Roo.DomHelper.append(document.body, {
38007                 tag: "pre", cls: "x-form-grow-sizer"
38008             });
38009             if(this.preventScrollbars){
38010                 this.el.setStyle("overflow", "hidden");
38011             }
38012             this.el.setHeight(this.growMin);
38013         }
38014     },
38015
38016     onDestroy : function(){
38017         if(this.textSizeEl){
38018             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38019         }
38020         Roo.form.TextArea.superclass.onDestroy.call(this);
38021     },
38022
38023     // private
38024     onKeyUp : function(e){
38025         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38026             this.autoSize();
38027         }
38028     },
38029
38030     /**
38031      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38032      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38033      */
38034     autoSize : function(){
38035         if(!this.grow || !this.textSizeEl){
38036             return;
38037         }
38038         var el = this.el;
38039         var v = el.dom.value;
38040         var ts = this.textSizeEl;
38041
38042         ts.innerHTML = '';
38043         ts.appendChild(document.createTextNode(v));
38044         v = ts.innerHTML;
38045
38046         Roo.fly(ts).setWidth(this.el.getWidth());
38047         if(v.length < 1){
38048             v = "&#160;&#160;";
38049         }else{
38050             if(Roo.isIE){
38051                 v = v.replace(/\n/g, '<p>&#160;</p>');
38052             }
38053             v += "&#160;\n&#160;";
38054         }
38055         ts.innerHTML = v;
38056         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38057         if(h != this.lastHeight){
38058             this.lastHeight = h;
38059             this.el.setHeight(h);
38060             this.fireEvent("autosize", this, h);
38061         }
38062     }
38063 });/*
38064  * Based on:
38065  * Ext JS Library 1.1.1
38066  * Copyright(c) 2006-2007, Ext JS, LLC.
38067  *
38068  * Originally Released Under LGPL - original licence link has changed is not relivant.
38069  *
38070  * Fork - LGPL
38071  * <script type="text/javascript">
38072  */
38073  
38074
38075 /**
38076  * @class Roo.form.NumberField
38077  * @extends Roo.form.TextField
38078  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38079  * @constructor
38080  * Creates a new NumberField
38081  * @param {Object} config Configuration options
38082  */
38083 Roo.form.NumberField = function(config){
38084     Roo.form.NumberField.superclass.constructor.call(this, config);
38085 };
38086
38087 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38088     /**
38089      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38090      */
38091     fieldClass: "x-form-field x-form-num-field",
38092     /**
38093      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38094      */
38095     allowDecimals : true,
38096     /**
38097      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38098      */
38099     decimalSeparator : ".",
38100     /**
38101      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38102      */
38103     decimalPrecision : 2,
38104     /**
38105      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38106      */
38107     allowNegative : true,
38108     /**
38109      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38110      */
38111     minValue : Number.NEGATIVE_INFINITY,
38112     /**
38113      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38114      */
38115     maxValue : Number.MAX_VALUE,
38116     /**
38117      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38118      */
38119     minText : "The minimum value for this field is {0}",
38120     /**
38121      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38122      */
38123     maxText : "The maximum value for this field is {0}",
38124     /**
38125      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38126      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38127      */
38128     nanText : "{0} is not a valid number",
38129
38130     // private
38131     initEvents : function(){
38132         Roo.form.NumberField.superclass.initEvents.call(this);
38133         var allowed = "0123456789";
38134         if(this.allowDecimals){
38135             allowed += this.decimalSeparator;
38136         }
38137         if(this.allowNegative){
38138             allowed += "-";
38139         }
38140         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38141         var keyPress = function(e){
38142             var k = e.getKey();
38143             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38144                 return;
38145             }
38146             var c = e.getCharCode();
38147             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38148                 e.stopEvent();
38149             }
38150         };
38151         this.el.on("keypress", keyPress, this);
38152     },
38153
38154     // private
38155     validateValue : function(value){
38156         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38157             return false;
38158         }
38159         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38160              return true;
38161         }
38162         var num = this.parseValue(value);
38163         if(isNaN(num)){
38164             this.markInvalid(String.format(this.nanText, value));
38165             return false;
38166         }
38167         if(num < this.minValue){
38168             this.markInvalid(String.format(this.minText, this.minValue));
38169             return false;
38170         }
38171         if(num > this.maxValue){
38172             this.markInvalid(String.format(this.maxText, this.maxValue));
38173             return false;
38174         }
38175         return true;
38176     },
38177
38178     getValue : function(){
38179         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38180     },
38181
38182     // private
38183     parseValue : function(value){
38184         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38185         return isNaN(value) ? '' : value;
38186     },
38187
38188     // private
38189     fixPrecision : function(value){
38190         var nan = isNaN(value);
38191         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38192             return nan ? '' : value;
38193         }
38194         return parseFloat(value).toFixed(this.decimalPrecision);
38195     },
38196
38197     setValue : function(v){
38198         v = this.fixPrecision(v);
38199         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38200     },
38201
38202     // private
38203     decimalPrecisionFcn : function(v){
38204         return Math.floor(v);
38205     },
38206
38207     beforeBlur : function(){
38208         var v = this.parseValue(this.getRawValue());
38209         if(v){
38210             this.setValue(v);
38211         }
38212     }
38213 });/*
38214  * Based on:
38215  * Ext JS Library 1.1.1
38216  * Copyright(c) 2006-2007, Ext JS, LLC.
38217  *
38218  * Originally Released Under LGPL - original licence link has changed is not relivant.
38219  *
38220  * Fork - LGPL
38221  * <script type="text/javascript">
38222  */
38223  
38224 /**
38225  * @class Roo.form.DateField
38226  * @extends Roo.form.TriggerField
38227  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38228 * @constructor
38229 * Create a new DateField
38230 * @param {Object} config
38231  */
38232 Roo.form.DateField = function(config){
38233     Roo.form.DateField.superclass.constructor.call(this, config);
38234     
38235       this.addEvents({
38236          
38237         /**
38238          * @event select
38239          * Fires when a date is selected
38240              * @param {Roo.form.DateField} combo This combo box
38241              * @param {Date} date The date selected
38242              */
38243         'select' : true
38244          
38245     });
38246     
38247     
38248     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38249     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38250     this.ddMatch = null;
38251     if(this.disabledDates){
38252         var dd = this.disabledDates;
38253         var re = "(?:";
38254         for(var i = 0; i < dd.length; i++){
38255             re += dd[i];
38256             if(i != dd.length-1) re += "|";
38257         }
38258         this.ddMatch = new RegExp(re + ")");
38259     }
38260 };
38261
38262 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38263     /**
38264      * @cfg {String} format
38265      * The default date format string which can be overriden for localization support.  The format must be
38266      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38267      */
38268     format : "m/d/y",
38269     /**
38270      * @cfg {String} altFormats
38271      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38272      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38273      */
38274     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38275     /**
38276      * @cfg {Array} disabledDays
38277      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38278      */
38279     disabledDays : null,
38280     /**
38281      * @cfg {String} disabledDaysText
38282      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38283      */
38284     disabledDaysText : "Disabled",
38285     /**
38286      * @cfg {Array} disabledDates
38287      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38288      * expression so they are very powerful. Some examples:
38289      * <ul>
38290      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38291      * <li>["03/08", "09/16"] would disable those days for every year</li>
38292      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38293      * <li>["03/../2006"] would disable every day in March 2006</li>
38294      * <li>["^03"] would disable every day in every March</li>
38295      * </ul>
38296      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38297      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38298      */
38299     disabledDates : null,
38300     /**
38301      * @cfg {String} disabledDatesText
38302      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38303      */
38304     disabledDatesText : "Disabled",
38305     /**
38306      * @cfg {Date/String} minValue
38307      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38308      * valid format (defaults to null).
38309      */
38310     minValue : null,
38311     /**
38312      * @cfg {Date/String} maxValue
38313      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38314      * valid format (defaults to null).
38315      */
38316     maxValue : null,
38317     /**
38318      * @cfg {String} minText
38319      * The error text to display when the date in the cell is before minValue (defaults to
38320      * 'The date in this field must be after {minValue}').
38321      */
38322     minText : "The date in this field must be equal to or after {0}",
38323     /**
38324      * @cfg {String} maxText
38325      * The error text to display when the date in the cell is after maxValue (defaults to
38326      * 'The date in this field must be before {maxValue}').
38327      */
38328     maxText : "The date in this field must be equal to or before {0}",
38329     /**
38330      * @cfg {String} invalidText
38331      * The error text to display when the date in the field is invalid (defaults to
38332      * '{value} is not a valid date - it must be in the format {format}').
38333      */
38334     invalidText : "{0} is not a valid date - it must be in the format {1}",
38335     /**
38336      * @cfg {String} triggerClass
38337      * An additional CSS class used to style the trigger button.  The trigger will always get the
38338      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38339      * which displays a calendar icon).
38340      */
38341     triggerClass : 'x-form-date-trigger',
38342     
38343
38344     /**
38345      * @cfg {Boolean} useIso
38346      * if enabled, then the date field will use a hidden field to store the 
38347      * real value as iso formated date. default (false)
38348      */ 
38349     useIso : false,
38350     /**
38351      * @cfg {String/Object} autoCreate
38352      * A DomHelper element spec, or true for a default element spec (defaults to
38353      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38354      */ 
38355     // private
38356     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38357     
38358     // private
38359     hiddenField: false,
38360     
38361     onRender : function(ct, position)
38362     {
38363         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38364         if (this.useIso) {
38365             //this.el.dom.removeAttribute('name'); 
38366             Roo.log("Changing name?");
38367             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38368             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38369                     'before', true);
38370             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38371             // prevent input submission
38372             this.hiddenName = this.name;
38373         }
38374             
38375             
38376     },
38377     
38378     // private
38379     validateValue : function(value)
38380     {
38381         value = this.formatDate(value);
38382         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38383             Roo.log('super failed');
38384             return false;
38385         }
38386         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38387              return true;
38388         }
38389         var svalue = value;
38390         value = this.parseDate(value);
38391         if(!value){
38392             Roo.log('parse date failed' + svalue);
38393             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38394             return false;
38395         }
38396         var time = value.getTime();
38397         if(this.minValue && time < this.minValue.getTime()){
38398             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38399             return false;
38400         }
38401         if(this.maxValue && time > this.maxValue.getTime()){
38402             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38403             return false;
38404         }
38405         if(this.disabledDays){
38406             var day = value.getDay();
38407             for(var i = 0; i < this.disabledDays.length; i++) {
38408                 if(day === this.disabledDays[i]){
38409                     this.markInvalid(this.disabledDaysText);
38410                     return false;
38411                 }
38412             }
38413         }
38414         var fvalue = this.formatDate(value);
38415         if(this.ddMatch && this.ddMatch.test(fvalue)){
38416             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38417             return false;
38418         }
38419         return true;
38420     },
38421
38422     // private
38423     // Provides logic to override the default TriggerField.validateBlur which just returns true
38424     validateBlur : function(){
38425         return !this.menu || !this.menu.isVisible();
38426     },
38427     
38428     getName: function()
38429     {
38430         // returns hidden if it's set..
38431         if (!this.rendered) {return ''};
38432         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38433         
38434     },
38435
38436     /**
38437      * Returns the current date value of the date field.
38438      * @return {Date} The date value
38439      */
38440     getValue : function(){
38441         
38442         return  this.hiddenField ?
38443                 this.hiddenField.value :
38444                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38445     },
38446
38447     /**
38448      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38449      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38450      * (the default format used is "m/d/y").
38451      * <br />Usage:
38452      * <pre><code>
38453 //All of these calls set the same date value (May 4, 2006)
38454
38455 //Pass a date object:
38456 var dt = new Date('5/4/06');
38457 dateField.setValue(dt);
38458
38459 //Pass a date string (default format):
38460 dateField.setValue('5/4/06');
38461
38462 //Pass a date string (custom format):
38463 dateField.format = 'Y-m-d';
38464 dateField.setValue('2006-5-4');
38465 </code></pre>
38466      * @param {String/Date} date The date or valid date string
38467      */
38468     setValue : function(date){
38469         if (this.hiddenField) {
38470             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38471         }
38472         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38473         // make sure the value field is always stored as a date..
38474         this.value = this.parseDate(date);
38475         
38476         
38477     },
38478
38479     // private
38480     parseDate : function(value){
38481         if(!value || value instanceof Date){
38482             return value;
38483         }
38484         var v = Date.parseDate(value, this.format);
38485          if (!v && this.useIso) {
38486             v = Date.parseDate(value, 'Y-m-d');
38487         }
38488         if(!v && this.altFormats){
38489             if(!this.altFormatsArray){
38490                 this.altFormatsArray = this.altFormats.split("|");
38491             }
38492             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38493                 v = Date.parseDate(value, this.altFormatsArray[i]);
38494             }
38495         }
38496         return v;
38497     },
38498
38499     // private
38500     formatDate : function(date, fmt){
38501         return (!date || !(date instanceof Date)) ?
38502                date : date.dateFormat(fmt || this.format);
38503     },
38504
38505     // private
38506     menuListeners : {
38507         select: function(m, d){
38508             
38509             this.setValue(d);
38510             this.fireEvent('select', this, d);
38511         },
38512         show : function(){ // retain focus styling
38513             this.onFocus();
38514         },
38515         hide : function(){
38516             this.focus.defer(10, this);
38517             var ml = this.menuListeners;
38518             this.menu.un("select", ml.select,  this);
38519             this.menu.un("show", ml.show,  this);
38520             this.menu.un("hide", ml.hide,  this);
38521         }
38522     },
38523
38524     // private
38525     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38526     onTriggerClick : function(){
38527         if(this.disabled){
38528             return;
38529         }
38530         if(this.menu == null){
38531             this.menu = new Roo.menu.DateMenu();
38532         }
38533         Roo.apply(this.menu.picker,  {
38534             showClear: this.allowBlank,
38535             minDate : this.minValue,
38536             maxDate : this.maxValue,
38537             disabledDatesRE : this.ddMatch,
38538             disabledDatesText : this.disabledDatesText,
38539             disabledDays : this.disabledDays,
38540             disabledDaysText : this.disabledDaysText,
38541             format : this.useIso ? 'Y-m-d' : this.format,
38542             minText : String.format(this.minText, this.formatDate(this.minValue)),
38543             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38544         });
38545         this.menu.on(Roo.apply({}, this.menuListeners, {
38546             scope:this
38547         }));
38548         this.menu.picker.setValue(this.getValue() || new Date());
38549         this.menu.show(this.el, "tl-bl?");
38550     },
38551
38552     beforeBlur : function(){
38553         var v = this.parseDate(this.getRawValue());
38554         if(v){
38555             this.setValue(v);
38556         }
38557     }
38558
38559     /** @cfg {Boolean} grow @hide */
38560     /** @cfg {Number} growMin @hide */
38561     /** @cfg {Number} growMax @hide */
38562     /**
38563      * @hide
38564      * @method autoSize
38565      */
38566 });/*
38567  * Based on:
38568  * Ext JS Library 1.1.1
38569  * Copyright(c) 2006-2007, Ext JS, LLC.
38570  *
38571  * Originally Released Under LGPL - original licence link has changed is not relivant.
38572  *
38573  * Fork - LGPL
38574  * <script type="text/javascript">
38575  */
38576  
38577 /**
38578  * @class Roo.form.MonthField
38579  * @extends Roo.form.TriggerField
38580  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38581 * @constructor
38582 * Create a new MonthField
38583 * @param {Object} config
38584  */
38585 Roo.form.MonthField = function(config){
38586     
38587     Roo.form.MonthField.superclass.constructor.call(this, config);
38588     
38589       this.addEvents({
38590          
38591         /**
38592          * @event select
38593          * Fires when a date is selected
38594              * @param {Roo.form.MonthFieeld} combo This combo box
38595              * @param {Date} date The date selected
38596              */
38597         'select' : true
38598          
38599     });
38600     
38601     
38602     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38603     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38604     this.ddMatch = null;
38605     if(this.disabledDates){
38606         var dd = this.disabledDates;
38607         var re = "(?:";
38608         for(var i = 0; i < dd.length; i++){
38609             re += dd[i];
38610             if(i != dd.length-1) re += "|";
38611         }
38612         this.ddMatch = new RegExp(re + ")");
38613     }
38614 };
38615
38616 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38617     /**
38618      * @cfg {String} format
38619      * The default date format string which can be overriden for localization support.  The format must be
38620      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38621      */
38622     format : "M Y",
38623     /**
38624      * @cfg {String} altFormats
38625      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38626      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38627      */
38628     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38629     /**
38630      * @cfg {Array} disabledDays
38631      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38632      */
38633     disabledDays : [0,1,2,3,4,5,6],
38634     /**
38635      * @cfg {String} disabledDaysText
38636      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38637      */
38638     disabledDaysText : "Disabled",
38639     /**
38640      * @cfg {Array} disabledDates
38641      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38642      * expression so they are very powerful. Some examples:
38643      * <ul>
38644      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38645      * <li>["03/08", "09/16"] would disable those days for every year</li>
38646      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38647      * <li>["03/../2006"] would disable every day in March 2006</li>
38648      * <li>["^03"] would disable every day in every March</li>
38649      * </ul>
38650      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38651      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38652      */
38653     disabledDates : null,
38654     /**
38655      * @cfg {String} disabledDatesText
38656      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38657      */
38658     disabledDatesText : "Disabled",
38659     /**
38660      * @cfg {Date/String} minValue
38661      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38662      * valid format (defaults to null).
38663      */
38664     minValue : null,
38665     /**
38666      * @cfg {Date/String} maxValue
38667      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38668      * valid format (defaults to null).
38669      */
38670     maxValue : null,
38671     /**
38672      * @cfg {String} minText
38673      * The error text to display when the date in the cell is before minValue (defaults to
38674      * 'The date in this field must be after {minValue}').
38675      */
38676     minText : "The date in this field must be equal to or after {0}",
38677     /**
38678      * @cfg {String} maxTextf
38679      * The error text to display when the date in the cell is after maxValue (defaults to
38680      * 'The date in this field must be before {maxValue}').
38681      */
38682     maxText : "The date in this field must be equal to or before {0}",
38683     /**
38684      * @cfg {String} invalidText
38685      * The error text to display when the date in the field is invalid (defaults to
38686      * '{value} is not a valid date - it must be in the format {format}').
38687      */
38688     invalidText : "{0} is not a valid date - it must be in the format {1}",
38689     /**
38690      * @cfg {String} triggerClass
38691      * An additional CSS class used to style the trigger button.  The trigger will always get the
38692      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38693      * which displays a calendar icon).
38694      */
38695     triggerClass : 'x-form-date-trigger',
38696     
38697
38698     /**
38699      * @cfg {Boolean} useIso
38700      * if enabled, then the date field will use a hidden field to store the 
38701      * real value as iso formated date. default (true)
38702      */ 
38703     useIso : true,
38704     /**
38705      * @cfg {String/Object} autoCreate
38706      * A DomHelper element spec, or true for a default element spec (defaults to
38707      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38708      */ 
38709     // private
38710     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38711     
38712     // private
38713     hiddenField: false,
38714     
38715     hideMonthPicker : false,
38716     
38717     onRender : function(ct, position)
38718     {
38719         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38720         if (this.useIso) {
38721             this.el.dom.removeAttribute('name'); 
38722             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38723                     'before', true);
38724             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38725             // prevent input submission
38726             this.hiddenName = this.name;
38727         }
38728             
38729             
38730     },
38731     
38732     // private
38733     validateValue : function(value)
38734     {
38735         value = this.formatDate(value);
38736         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38737             return false;
38738         }
38739         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38740              return true;
38741         }
38742         var svalue = value;
38743         value = this.parseDate(value);
38744         if(!value){
38745             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38746             return false;
38747         }
38748         var time = value.getTime();
38749         if(this.minValue && time < this.minValue.getTime()){
38750             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38751             return false;
38752         }
38753         if(this.maxValue && time > this.maxValue.getTime()){
38754             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38755             return false;
38756         }
38757         /*if(this.disabledDays){
38758             var day = value.getDay();
38759             for(var i = 0; i < this.disabledDays.length; i++) {
38760                 if(day === this.disabledDays[i]){
38761                     this.markInvalid(this.disabledDaysText);
38762                     return false;
38763                 }
38764             }
38765         }
38766         */
38767         var fvalue = this.formatDate(value);
38768         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38769             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38770             return false;
38771         }
38772         */
38773         return true;
38774     },
38775
38776     // private
38777     // Provides logic to override the default TriggerField.validateBlur which just returns true
38778     validateBlur : function(){
38779         return !this.menu || !this.menu.isVisible();
38780     },
38781
38782     /**
38783      * Returns the current date value of the date field.
38784      * @return {Date} The date value
38785      */
38786     getValue : function(){
38787         
38788         
38789         
38790         return  this.hiddenField ?
38791                 this.hiddenField.value :
38792                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38793     },
38794
38795     /**
38796      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38797      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38798      * (the default format used is "m/d/y").
38799      * <br />Usage:
38800      * <pre><code>
38801 //All of these calls set the same date value (May 4, 2006)
38802
38803 //Pass a date object:
38804 var dt = new Date('5/4/06');
38805 monthField.setValue(dt);
38806
38807 //Pass a date string (default format):
38808 monthField.setValue('5/4/06');
38809
38810 //Pass a date string (custom format):
38811 monthField.format = 'Y-m-d';
38812 monthField.setValue('2006-5-4');
38813 </code></pre>
38814      * @param {String/Date} date The date or valid date string
38815      */
38816     setValue : function(date){
38817         Roo.log('month setValue' + date);
38818         // can only be first of month..
38819         
38820         var val = this.parseDate(date);
38821         
38822         if (this.hiddenField) {
38823             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38824         }
38825         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38826         this.value = this.parseDate(date);
38827     },
38828
38829     // private
38830     parseDate : function(value){
38831         if(!value || value instanceof Date){
38832             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38833             return value;
38834         }
38835         var v = Date.parseDate(value, this.format);
38836         if (!v && this.useIso) {
38837             v = Date.parseDate(value, 'Y-m-d');
38838         }
38839         if (v) {
38840             // 
38841             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38842         }
38843         
38844         
38845         if(!v && this.altFormats){
38846             if(!this.altFormatsArray){
38847                 this.altFormatsArray = this.altFormats.split("|");
38848             }
38849             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38850                 v = Date.parseDate(value, this.altFormatsArray[i]);
38851             }
38852         }
38853         return v;
38854     },
38855
38856     // private
38857     formatDate : function(date, fmt){
38858         return (!date || !(date instanceof Date)) ?
38859                date : date.dateFormat(fmt || this.format);
38860     },
38861
38862     // private
38863     menuListeners : {
38864         select: function(m, d){
38865             this.setValue(d);
38866             this.fireEvent('select', this, d);
38867         },
38868         show : function(){ // retain focus styling
38869             this.onFocus();
38870         },
38871         hide : function(){
38872             this.focus.defer(10, this);
38873             var ml = this.menuListeners;
38874             this.menu.un("select", ml.select,  this);
38875             this.menu.un("show", ml.show,  this);
38876             this.menu.un("hide", ml.hide,  this);
38877         }
38878     },
38879     // private
38880     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38881     onTriggerClick : function(){
38882         if(this.disabled){
38883             return;
38884         }
38885         if(this.menu == null){
38886             this.menu = new Roo.menu.DateMenu();
38887            
38888         }
38889         
38890         Roo.apply(this.menu.picker,  {
38891             
38892             showClear: this.allowBlank,
38893             minDate : this.minValue,
38894             maxDate : this.maxValue,
38895             disabledDatesRE : this.ddMatch,
38896             disabledDatesText : this.disabledDatesText,
38897             
38898             format : this.useIso ? 'Y-m-d' : this.format,
38899             minText : String.format(this.minText, this.formatDate(this.minValue)),
38900             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38901             
38902         });
38903          this.menu.on(Roo.apply({}, this.menuListeners, {
38904             scope:this
38905         }));
38906        
38907         
38908         var m = this.menu;
38909         var p = m.picker;
38910         
38911         // hide month picker get's called when we called by 'before hide';
38912         
38913         var ignorehide = true;
38914         p.hideMonthPicker  = function(disableAnim){
38915             if (ignorehide) {
38916                 return;
38917             }
38918              if(this.monthPicker){
38919                 Roo.log("hideMonthPicker called");
38920                 if(disableAnim === true){
38921                     this.monthPicker.hide();
38922                 }else{
38923                     this.monthPicker.slideOut('t', {duration:.2});
38924                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38925                     p.fireEvent("select", this, this.value);
38926                     m.hide();
38927                 }
38928             }
38929         }
38930         
38931         Roo.log('picker set value');
38932         Roo.log(this.getValue());
38933         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38934         m.show(this.el, 'tl-bl?');
38935         ignorehide  = false;
38936         // this will trigger hideMonthPicker..
38937         
38938         
38939         // hidden the day picker
38940         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38941         
38942         
38943         
38944       
38945         
38946         p.showMonthPicker.defer(100, p);
38947     
38948         
38949        
38950     },
38951
38952     beforeBlur : function(){
38953         var v = this.parseDate(this.getRawValue());
38954         if(v){
38955             this.setValue(v);
38956         }
38957     }
38958
38959     /** @cfg {Boolean} grow @hide */
38960     /** @cfg {Number} growMin @hide */
38961     /** @cfg {Number} growMax @hide */
38962     /**
38963      * @hide
38964      * @method autoSize
38965      */
38966 });/*
38967  * Based on:
38968  * Ext JS Library 1.1.1
38969  * Copyright(c) 2006-2007, Ext JS, LLC.
38970  *
38971  * Originally Released Under LGPL - original licence link has changed is not relivant.
38972  *
38973  * Fork - LGPL
38974  * <script type="text/javascript">
38975  */
38976  
38977
38978 /**
38979  * @class Roo.form.ComboBox
38980  * @extends Roo.form.TriggerField
38981  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38982  * @constructor
38983  * Create a new ComboBox.
38984  * @param {Object} config Configuration options
38985  */
38986 Roo.form.ComboBox = function(config){
38987     Roo.form.ComboBox.superclass.constructor.call(this, config);
38988     this.addEvents({
38989         /**
38990          * @event expand
38991          * Fires when the dropdown list is expanded
38992              * @param {Roo.form.ComboBox} combo This combo box
38993              */
38994         'expand' : true,
38995         /**
38996          * @event collapse
38997          * Fires when the dropdown list is collapsed
38998              * @param {Roo.form.ComboBox} combo This combo box
38999              */
39000         'collapse' : true,
39001         /**
39002          * @event beforeselect
39003          * Fires before a list item is selected. Return false to cancel the selection.
39004              * @param {Roo.form.ComboBox} combo This combo box
39005              * @param {Roo.data.Record} record The data record returned from the underlying store
39006              * @param {Number} index The index of the selected item in the dropdown list
39007              */
39008         'beforeselect' : true,
39009         /**
39010          * @event select
39011          * Fires when a list item is selected
39012              * @param {Roo.form.ComboBox} combo This combo box
39013              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39014              * @param {Number} index The index of the selected item in the dropdown list
39015              */
39016         'select' : true,
39017         /**
39018          * @event beforequery
39019          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39020          * The event object passed has these properties:
39021              * @param {Roo.form.ComboBox} combo This combo box
39022              * @param {String} query The query
39023              * @param {Boolean} forceAll true to force "all" query
39024              * @param {Boolean} cancel true to cancel the query
39025              * @param {Object} e The query event object
39026              */
39027         'beforequery': true,
39028          /**
39029          * @event add
39030          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39031              * @param {Roo.form.ComboBox} combo This combo box
39032              */
39033         'add' : true,
39034         /**
39035          * @event edit
39036          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39037              * @param {Roo.form.ComboBox} combo This combo box
39038              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39039              */
39040         'edit' : true
39041         
39042         
39043     });
39044     if(this.transform){
39045         this.allowDomMove = false;
39046         var s = Roo.getDom(this.transform);
39047         if(!this.hiddenName){
39048             this.hiddenName = s.name;
39049         }
39050         if(!this.store){
39051             this.mode = 'local';
39052             var d = [], opts = s.options;
39053             for(var i = 0, len = opts.length;i < len; i++){
39054                 var o = opts[i];
39055                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39056                 if(o.selected) {
39057                     this.value = value;
39058                 }
39059                 d.push([value, o.text]);
39060             }
39061             this.store = new Roo.data.SimpleStore({
39062                 'id': 0,
39063                 fields: ['value', 'text'],
39064                 data : d
39065             });
39066             this.valueField = 'value';
39067             this.displayField = 'text';
39068         }
39069         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39070         if(!this.lazyRender){
39071             this.target = true;
39072             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39073             s.parentNode.removeChild(s); // remove it
39074             this.render(this.el.parentNode);
39075         }else{
39076             s.parentNode.removeChild(s); // remove it
39077         }
39078
39079     }
39080     if (this.store) {
39081         this.store = Roo.factory(this.store, Roo.data);
39082     }
39083     
39084     this.selectedIndex = -1;
39085     if(this.mode == 'local'){
39086         if(config.queryDelay === undefined){
39087             this.queryDelay = 10;
39088         }
39089         if(config.minChars === undefined){
39090             this.minChars = 0;
39091         }
39092     }
39093 };
39094
39095 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39096     /**
39097      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39098      */
39099     /**
39100      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39101      * rendering into an Roo.Editor, defaults to false)
39102      */
39103     /**
39104      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39105      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39106      */
39107     /**
39108      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39109      */
39110     /**
39111      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39112      * the dropdown list (defaults to undefined, with no header element)
39113      */
39114
39115      /**
39116      * @cfg {String/Roo.Template} tpl The template to use to render the output
39117      */
39118      
39119     // private
39120     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39121     /**
39122      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39123      */
39124     listWidth: undefined,
39125     /**
39126      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39127      * mode = 'remote' or 'text' if mode = 'local')
39128      */
39129     displayField: undefined,
39130     /**
39131      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39132      * mode = 'remote' or 'value' if mode = 'local'). 
39133      * Note: use of a valueField requires the user make a selection
39134      * in order for a value to be mapped.
39135      */
39136     valueField: undefined,
39137     
39138     
39139     /**
39140      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39141      * field's data value (defaults to the underlying DOM element's name)
39142      */
39143     hiddenName: undefined,
39144     /**
39145      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39146      */
39147     listClass: '',
39148     /**
39149      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39150      */
39151     selectedClass: 'x-combo-selected',
39152     /**
39153      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39154      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39155      * which displays a downward arrow icon).
39156      */
39157     triggerClass : 'x-form-arrow-trigger',
39158     /**
39159      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39160      */
39161     shadow:'sides',
39162     /**
39163      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39164      * anchor positions (defaults to 'tl-bl')
39165      */
39166     listAlign: 'tl-bl?',
39167     /**
39168      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39169      */
39170     maxHeight: 300,
39171     /**
39172      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39173      * query specified by the allQuery config option (defaults to 'query')
39174      */
39175     triggerAction: 'query',
39176     /**
39177      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39178      * (defaults to 4, does not apply if editable = false)
39179      */
39180     minChars : 4,
39181     /**
39182      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39183      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39184      */
39185     typeAhead: false,
39186     /**
39187      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39188      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39189      */
39190     queryDelay: 500,
39191     /**
39192      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39193      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39194      */
39195     pageSize: 0,
39196     /**
39197      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39198      * when editable = true (defaults to false)
39199      */
39200     selectOnFocus:false,
39201     /**
39202      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39203      */
39204     queryParam: 'query',
39205     /**
39206      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39207      * when mode = 'remote' (defaults to 'Loading...')
39208      */
39209     loadingText: 'Loading...',
39210     /**
39211      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39212      */
39213     resizable: false,
39214     /**
39215      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39216      */
39217     handleHeight : 8,
39218     /**
39219      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39220      * traditional select (defaults to true)
39221      */
39222     editable: true,
39223     /**
39224      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39225      */
39226     allQuery: '',
39227     /**
39228      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39229      */
39230     mode: 'remote',
39231     /**
39232      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39233      * listWidth has a higher value)
39234      */
39235     minListWidth : 70,
39236     /**
39237      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39238      * allow the user to set arbitrary text into the field (defaults to false)
39239      */
39240     forceSelection:false,
39241     /**
39242      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39243      * if typeAhead = true (defaults to 250)
39244      */
39245     typeAheadDelay : 250,
39246     /**
39247      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39248      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39249      */
39250     valueNotFoundText : undefined,
39251     /**
39252      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39253      */
39254     blockFocus : false,
39255     
39256     /**
39257      * @cfg {Boolean} disableClear Disable showing of clear button.
39258      */
39259     disableClear : false,
39260     /**
39261      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39262      */
39263     alwaysQuery : false,
39264     
39265     //private
39266     addicon : false,
39267     editicon: false,
39268     
39269     // element that contains real text value.. (when hidden is used..)
39270      
39271     // private
39272     onRender : function(ct, position){
39273         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39274         if(this.hiddenName){
39275             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39276                     'before', true);
39277             this.hiddenField.value =
39278                 this.hiddenValue !== undefined ? this.hiddenValue :
39279                 this.value !== undefined ? this.value : '';
39280
39281             // prevent input submission
39282             this.el.dom.removeAttribute('name');
39283              
39284              
39285         }
39286         if(Roo.isGecko){
39287             this.el.dom.setAttribute('autocomplete', 'off');
39288         }
39289
39290         var cls = 'x-combo-list';
39291
39292         this.list = new Roo.Layer({
39293             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39294         });
39295
39296         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39297         this.list.setWidth(lw);
39298         this.list.swallowEvent('mousewheel');
39299         this.assetHeight = 0;
39300
39301         if(this.title){
39302             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39303             this.assetHeight += this.header.getHeight();
39304         }
39305
39306         this.innerList = this.list.createChild({cls:cls+'-inner'});
39307         this.innerList.on('mouseover', this.onViewOver, this);
39308         this.innerList.on('mousemove', this.onViewMove, this);
39309         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39310         
39311         if(this.allowBlank && !this.pageSize && !this.disableClear){
39312             this.footer = this.list.createChild({cls:cls+'-ft'});
39313             this.pageTb = new Roo.Toolbar(this.footer);
39314            
39315         }
39316         if(this.pageSize){
39317             this.footer = this.list.createChild({cls:cls+'-ft'});
39318             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39319                     {pageSize: this.pageSize});
39320             
39321         }
39322         
39323         if (this.pageTb && this.allowBlank && !this.disableClear) {
39324             var _this = this;
39325             this.pageTb.add(new Roo.Toolbar.Fill(), {
39326                 cls: 'x-btn-icon x-btn-clear',
39327                 text: '&#160;',
39328                 handler: function()
39329                 {
39330                     _this.collapse();
39331                     _this.clearValue();
39332                     _this.onSelect(false, -1);
39333                 }
39334             });
39335         }
39336         if (this.footer) {
39337             this.assetHeight += this.footer.getHeight();
39338         }
39339         
39340
39341         if(!this.tpl){
39342             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39343         }
39344
39345         this.view = new Roo.View(this.innerList, this.tpl, {
39346             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39347         });
39348
39349         this.view.on('click', this.onViewClick, this);
39350
39351         this.store.on('beforeload', this.onBeforeLoad, this);
39352         this.store.on('load', this.onLoad, this);
39353         this.store.on('loadexception', this.onLoadException, this);
39354
39355         if(this.resizable){
39356             this.resizer = new Roo.Resizable(this.list,  {
39357                pinned:true, handles:'se'
39358             });
39359             this.resizer.on('resize', function(r, w, h){
39360                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39361                 this.listWidth = w;
39362                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39363                 this.restrictHeight();
39364             }, this);
39365             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39366         }
39367         if(!this.editable){
39368             this.editable = true;
39369             this.setEditable(false);
39370         }  
39371         
39372         
39373         if (typeof(this.events.add.listeners) != 'undefined') {
39374             
39375             this.addicon = this.wrap.createChild(
39376                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39377        
39378             this.addicon.on('click', function(e) {
39379                 this.fireEvent('add', this);
39380             }, this);
39381         }
39382         if (typeof(this.events.edit.listeners) != 'undefined') {
39383             
39384             this.editicon = this.wrap.createChild(
39385                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39386             if (this.addicon) {
39387                 this.editicon.setStyle('margin-left', '40px');
39388             }
39389             this.editicon.on('click', function(e) {
39390                 
39391                 // we fire even  if inothing is selected..
39392                 this.fireEvent('edit', this, this.lastData );
39393                 
39394             }, this);
39395         }
39396         
39397         
39398         
39399     },
39400
39401     // private
39402     initEvents : function(){
39403         Roo.form.ComboBox.superclass.initEvents.call(this);
39404
39405         this.keyNav = new Roo.KeyNav(this.el, {
39406             "up" : function(e){
39407                 this.inKeyMode = true;
39408                 this.selectPrev();
39409             },
39410
39411             "down" : function(e){
39412                 if(!this.isExpanded()){
39413                     this.onTriggerClick();
39414                 }else{
39415                     this.inKeyMode = true;
39416                     this.selectNext();
39417                 }
39418             },
39419
39420             "enter" : function(e){
39421                 this.onViewClick();
39422                 //return true;
39423             },
39424
39425             "esc" : function(e){
39426                 this.collapse();
39427             },
39428
39429             "tab" : function(e){
39430                 this.onViewClick(false);
39431                 this.fireEvent("specialkey", this, e);
39432                 return true;
39433             },
39434
39435             scope : this,
39436
39437             doRelay : function(foo, bar, hname){
39438                 if(hname == 'down' || this.scope.isExpanded()){
39439                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39440                 }
39441                 return true;
39442             },
39443
39444             forceKeyDown: true
39445         });
39446         this.queryDelay = Math.max(this.queryDelay || 10,
39447                 this.mode == 'local' ? 10 : 250);
39448         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39449         if(this.typeAhead){
39450             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39451         }
39452         if(this.editable !== false){
39453             this.el.on("keyup", this.onKeyUp, this);
39454         }
39455         if(this.forceSelection){
39456             this.on('blur', this.doForce, this);
39457         }
39458     },
39459
39460     onDestroy : function(){
39461         if(this.view){
39462             this.view.setStore(null);
39463             this.view.el.removeAllListeners();
39464             this.view.el.remove();
39465             this.view.purgeListeners();
39466         }
39467         if(this.list){
39468             this.list.destroy();
39469         }
39470         if(this.store){
39471             this.store.un('beforeload', this.onBeforeLoad, this);
39472             this.store.un('load', this.onLoad, this);
39473             this.store.un('loadexception', this.onLoadException, this);
39474         }
39475         Roo.form.ComboBox.superclass.onDestroy.call(this);
39476     },
39477
39478     // private
39479     fireKey : function(e){
39480         if(e.isNavKeyPress() && !this.list.isVisible()){
39481             this.fireEvent("specialkey", this, e);
39482         }
39483     },
39484
39485     // private
39486     onResize: function(w, h){
39487         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39488         
39489         if(typeof w != 'number'){
39490             // we do not handle it!?!?
39491             return;
39492         }
39493         var tw = this.trigger.getWidth();
39494         tw += this.addicon ? this.addicon.getWidth() : 0;
39495         tw += this.editicon ? this.editicon.getWidth() : 0;
39496         var x = w - tw;
39497         this.el.setWidth( this.adjustWidth('input', x));
39498             
39499         this.trigger.setStyle('left', x+'px');
39500         
39501         if(this.list && this.listWidth === undefined){
39502             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39503             this.list.setWidth(lw);
39504             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39505         }
39506         
39507     
39508         
39509     },
39510
39511     /**
39512      * Allow or prevent the user from directly editing the field text.  If false is passed,
39513      * the user will only be able to select from the items defined in the dropdown list.  This method
39514      * is the runtime equivalent of setting the 'editable' config option at config time.
39515      * @param {Boolean} value True to allow the user to directly edit the field text
39516      */
39517     setEditable : function(value){
39518         if(value == this.editable){
39519             return;
39520         }
39521         this.editable = value;
39522         if(!value){
39523             this.el.dom.setAttribute('readOnly', true);
39524             this.el.on('mousedown', this.onTriggerClick,  this);
39525             this.el.addClass('x-combo-noedit');
39526         }else{
39527             this.el.dom.setAttribute('readOnly', false);
39528             this.el.un('mousedown', this.onTriggerClick,  this);
39529             this.el.removeClass('x-combo-noedit');
39530         }
39531     },
39532
39533     // private
39534     onBeforeLoad : function(){
39535         if(!this.hasFocus){
39536             return;
39537         }
39538         this.innerList.update(this.loadingText ?
39539                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39540         this.restrictHeight();
39541         this.selectedIndex = -1;
39542     },
39543
39544     // private
39545     onLoad : function(){
39546         if(!this.hasFocus){
39547             return;
39548         }
39549         if(this.store.getCount() > 0){
39550             this.expand();
39551             this.restrictHeight();
39552             if(this.lastQuery == this.allQuery){
39553                 if(this.editable){
39554                     this.el.dom.select();
39555                 }
39556                 if(!this.selectByValue(this.value, true)){
39557                     this.select(0, true);
39558                 }
39559             }else{
39560                 this.selectNext();
39561                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39562                     this.taTask.delay(this.typeAheadDelay);
39563                 }
39564             }
39565         }else{
39566             this.onEmptyResults();
39567         }
39568         //this.el.focus();
39569     },
39570     // private
39571     onLoadException : function()
39572     {
39573         this.collapse();
39574         Roo.log(this.store.reader.jsonData);
39575         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39576             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39577         }
39578         
39579         
39580     },
39581     // private
39582     onTypeAhead : function(){
39583         if(this.store.getCount() > 0){
39584             var r = this.store.getAt(0);
39585             var newValue = r.data[this.displayField];
39586             var len = newValue.length;
39587             var selStart = this.getRawValue().length;
39588             if(selStart != len){
39589                 this.setRawValue(newValue);
39590                 this.selectText(selStart, newValue.length);
39591             }
39592         }
39593     },
39594
39595     // private
39596     onSelect : function(record, index){
39597         if(this.fireEvent('beforeselect', this, record, index) !== false){
39598             this.setFromData(index > -1 ? record.data : false);
39599             this.collapse();
39600             this.fireEvent('select', this, record, index);
39601         }
39602     },
39603
39604     /**
39605      * Returns the currently selected field value or empty string if no value is set.
39606      * @return {String} value The selected value
39607      */
39608     getValue : function(){
39609         if(this.valueField){
39610             return typeof this.value != 'undefined' ? this.value : '';
39611         }else{
39612             return Roo.form.ComboBox.superclass.getValue.call(this);
39613         }
39614     },
39615
39616     /**
39617      * Clears any text/value currently set in the field
39618      */
39619     clearValue : function(){
39620         if(this.hiddenField){
39621             this.hiddenField.value = '';
39622         }
39623         this.value = '';
39624         this.setRawValue('');
39625         this.lastSelectionText = '';
39626         
39627     },
39628
39629     /**
39630      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39631      * will be displayed in the field.  If the value does not match the data value of an existing item,
39632      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39633      * Otherwise the field will be blank (although the value will still be set).
39634      * @param {String} value The value to match
39635      */
39636     setValue : function(v){
39637         var text = v;
39638         if(this.valueField){
39639             var r = this.findRecord(this.valueField, v);
39640             if(r){
39641                 text = r.data[this.displayField];
39642             }else if(this.valueNotFoundText !== undefined){
39643                 text = this.valueNotFoundText;
39644             }
39645         }
39646         this.lastSelectionText = text;
39647         if(this.hiddenField){
39648             this.hiddenField.value = v;
39649         }
39650         Roo.form.ComboBox.superclass.setValue.call(this, text);
39651         this.value = v;
39652     },
39653     /**
39654      * @property {Object} the last set data for the element
39655      */
39656     
39657     lastData : false,
39658     /**
39659      * Sets the value of the field based on a object which is related to the record format for the store.
39660      * @param {Object} value the value to set as. or false on reset?
39661      */
39662     setFromData : function(o){
39663         var dv = ''; // display value
39664         var vv = ''; // value value..
39665         this.lastData = o;
39666         if (this.displayField) {
39667             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39668         } else {
39669             // this is an error condition!!!
39670             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39671         }
39672         
39673         if(this.valueField){
39674             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39675         }
39676         if(this.hiddenField){
39677             this.hiddenField.value = vv;
39678             
39679             this.lastSelectionText = dv;
39680             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39681             this.value = vv;
39682             return;
39683         }
39684         // no hidden field.. - we store the value in 'value', but still display
39685         // display field!!!!
39686         this.lastSelectionText = dv;
39687         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39688         this.value = vv;
39689         
39690         
39691     },
39692     // private
39693     reset : function(){
39694         // overridden so that last data is reset..
39695         this.setValue(this.originalValue);
39696         this.clearInvalid();
39697         this.lastData = false;
39698         if (this.view) {
39699             this.view.clearSelections();
39700         }
39701     },
39702     // private
39703     findRecord : function(prop, value){
39704         var record;
39705         if(this.store.getCount() > 0){
39706             this.store.each(function(r){
39707                 if(r.data[prop] == value){
39708                     record = r;
39709                     return false;
39710                 }
39711                 return true;
39712             });
39713         }
39714         return record;
39715     },
39716     
39717     getName: function()
39718     {
39719         // returns hidden if it's set..
39720         if (!this.rendered) {return ''};
39721         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39722         
39723     },
39724     // private
39725     onViewMove : function(e, t){
39726         this.inKeyMode = false;
39727     },
39728
39729     // private
39730     onViewOver : function(e, t){
39731         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39732             return;
39733         }
39734         var item = this.view.findItemFromChild(t);
39735         if(item){
39736             var index = this.view.indexOf(item);
39737             this.select(index, false);
39738         }
39739     },
39740
39741     // private
39742     onViewClick : function(doFocus)
39743     {
39744         var index = this.view.getSelectedIndexes()[0];
39745         var r = this.store.getAt(index);
39746         if(r){
39747             this.onSelect(r, index);
39748         }
39749         if(doFocus !== false && !this.blockFocus){
39750             this.el.focus();
39751         }
39752     },
39753
39754     // private
39755     restrictHeight : function(){
39756         this.innerList.dom.style.height = '';
39757         var inner = this.innerList.dom;
39758         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39759         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39760         this.list.beginUpdate();
39761         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39762         this.list.alignTo(this.el, this.listAlign);
39763         this.list.endUpdate();
39764     },
39765
39766     // private
39767     onEmptyResults : function(){
39768         this.collapse();
39769     },
39770
39771     /**
39772      * Returns true if the dropdown list is expanded, else false.
39773      */
39774     isExpanded : function(){
39775         return this.list.isVisible();
39776     },
39777
39778     /**
39779      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39780      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39781      * @param {String} value The data value of the item to select
39782      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39783      * selected item if it is not currently in view (defaults to true)
39784      * @return {Boolean} True if the value matched an item in the list, else false
39785      */
39786     selectByValue : function(v, scrollIntoView){
39787         if(v !== undefined && v !== null){
39788             var r = this.findRecord(this.valueField || this.displayField, v);
39789             if(r){
39790                 this.select(this.store.indexOf(r), scrollIntoView);
39791                 return true;
39792             }
39793         }
39794         return false;
39795     },
39796
39797     /**
39798      * Select an item in the dropdown list by its numeric index in the list. 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 {Number} index The zero-based index of the list 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      */
39804     select : function(index, scrollIntoView){
39805         this.selectedIndex = index;
39806         this.view.select(index);
39807         if(scrollIntoView !== false){
39808             var el = this.view.getNode(index);
39809             if(el){
39810                 this.innerList.scrollChildIntoView(el, false);
39811             }
39812         }
39813     },
39814
39815     // private
39816     selectNext : function(){
39817         var ct = this.store.getCount();
39818         if(ct > 0){
39819             if(this.selectedIndex == -1){
39820                 this.select(0);
39821             }else if(this.selectedIndex < ct-1){
39822                 this.select(this.selectedIndex+1);
39823             }
39824         }
39825     },
39826
39827     // private
39828     selectPrev : function(){
39829         var ct = this.store.getCount();
39830         if(ct > 0){
39831             if(this.selectedIndex == -1){
39832                 this.select(0);
39833             }else if(this.selectedIndex != 0){
39834                 this.select(this.selectedIndex-1);
39835             }
39836         }
39837     },
39838
39839     // private
39840     onKeyUp : function(e){
39841         if(this.editable !== false && !e.isSpecialKey()){
39842             this.lastKey = e.getKey();
39843             this.dqTask.delay(this.queryDelay);
39844         }
39845     },
39846
39847     // private
39848     validateBlur : function(){
39849         return !this.list || !this.list.isVisible();   
39850     },
39851
39852     // private
39853     initQuery : function(){
39854         this.doQuery(this.getRawValue());
39855     },
39856
39857     // private
39858     doForce : function(){
39859         if(this.el.dom.value.length > 0){
39860             this.el.dom.value =
39861                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39862              
39863         }
39864     },
39865
39866     /**
39867      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39868      * query allowing the query action to be canceled if needed.
39869      * @param {String} query The SQL query to execute
39870      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39871      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39872      * saved in the current store (defaults to false)
39873      */
39874     doQuery : function(q, forceAll){
39875         if(q === undefined || q === null){
39876             q = '';
39877         }
39878         var qe = {
39879             query: q,
39880             forceAll: forceAll,
39881             combo: this,
39882             cancel:false
39883         };
39884         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39885             return false;
39886         }
39887         q = qe.query;
39888         forceAll = qe.forceAll;
39889         if(forceAll === true || (q.length >= this.minChars)){
39890             if(this.lastQuery != q || this.alwaysQuery){
39891                 this.lastQuery = q;
39892                 if(this.mode == 'local'){
39893                     this.selectedIndex = -1;
39894                     if(forceAll){
39895                         this.store.clearFilter();
39896                     }else{
39897                         this.store.filter(this.displayField, q);
39898                     }
39899                     this.onLoad();
39900                 }else{
39901                     this.store.baseParams[this.queryParam] = q;
39902                     this.store.load({
39903                         params: this.getParams(q)
39904                     });
39905                     this.expand();
39906                 }
39907             }else{
39908                 this.selectedIndex = -1;
39909                 this.onLoad();   
39910             }
39911         }
39912     },
39913
39914     // private
39915     getParams : function(q){
39916         var p = {};
39917         //p[this.queryParam] = q;
39918         if(this.pageSize){
39919             p.start = 0;
39920             p.limit = this.pageSize;
39921         }
39922         return p;
39923     },
39924
39925     /**
39926      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39927      */
39928     collapse : function(){
39929         if(!this.isExpanded()){
39930             return;
39931         }
39932         this.list.hide();
39933         Roo.get(document).un('mousedown', this.collapseIf, this);
39934         Roo.get(document).un('mousewheel', this.collapseIf, this);
39935         if (!this.editable) {
39936             Roo.get(document).un('keydown', this.listKeyPress, this);
39937         }
39938         this.fireEvent('collapse', this);
39939     },
39940
39941     // private
39942     collapseIf : function(e){
39943         if(!e.within(this.wrap) && !e.within(this.list)){
39944             this.collapse();
39945         }
39946     },
39947
39948     /**
39949      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39950      */
39951     expand : function(){
39952         if(this.isExpanded() || !this.hasFocus){
39953             return;
39954         }
39955         this.list.alignTo(this.el, this.listAlign);
39956         this.list.show();
39957         Roo.get(document).on('mousedown', this.collapseIf, this);
39958         Roo.get(document).on('mousewheel', this.collapseIf, this);
39959         if (!this.editable) {
39960             Roo.get(document).on('keydown', this.listKeyPress, this);
39961         }
39962         
39963         this.fireEvent('expand', this);
39964     },
39965
39966     // private
39967     // Implements the default empty TriggerField.onTriggerClick function
39968     onTriggerClick : function(){
39969         if(this.disabled){
39970             return;
39971         }
39972         if(this.isExpanded()){
39973             this.collapse();
39974             if (!this.blockFocus) {
39975                 this.el.focus();
39976             }
39977             
39978         }else {
39979             this.hasFocus = true;
39980             if(this.triggerAction == 'all') {
39981                 this.doQuery(this.allQuery, true);
39982             } else {
39983                 this.doQuery(this.getRawValue());
39984             }
39985             if (!this.blockFocus) {
39986                 this.el.focus();
39987             }
39988         }
39989     },
39990     listKeyPress : function(e)
39991     {
39992         //Roo.log('listkeypress');
39993         // scroll to first matching element based on key pres..
39994         if (e.isSpecialKey()) {
39995             return false;
39996         }
39997         var k = String.fromCharCode(e.getKey()).toUpperCase();
39998         //Roo.log(k);
39999         var match  = false;
40000         var csel = this.view.getSelectedNodes();
40001         var cselitem = false;
40002         if (csel.length) {
40003             var ix = this.view.indexOf(csel[0]);
40004             cselitem  = this.store.getAt(ix);
40005             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40006                 cselitem = false;
40007             }
40008             
40009         }
40010         
40011         this.store.each(function(v) { 
40012             if (cselitem) {
40013                 // start at existing selection.
40014                 if (cselitem.id == v.id) {
40015                     cselitem = false;
40016                 }
40017                 return;
40018             }
40019                 
40020             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40021                 match = this.store.indexOf(v);
40022                 return false;
40023             }
40024         }, this);
40025         
40026         if (match === false) {
40027             return true; // no more action?
40028         }
40029         // scroll to?
40030         this.view.select(match);
40031         var sn = Roo.get(this.view.getSelectedNodes()[0])
40032         sn.scrollIntoView(sn.dom.parentNode, false);
40033     }
40034
40035     /** 
40036     * @cfg {Boolean} grow 
40037     * @hide 
40038     */
40039     /** 
40040     * @cfg {Number} growMin 
40041     * @hide 
40042     */
40043     /** 
40044     * @cfg {Number} growMax 
40045     * @hide 
40046     */
40047     /**
40048      * @hide
40049      * @method autoSize
40050      */
40051 });/*
40052  * Copyright(c) 2010-2012, Roo J Solutions Limited
40053  *
40054  * Licence LGPL
40055  *
40056  */
40057
40058 /**
40059  * @class Roo.form.ComboBoxArray
40060  * @extends Roo.form.TextField
40061  * A facebook style adder... for lists of email / people / countries  etc...
40062  * pick multiple items from a combo box, and shows each one.
40063  *
40064  *  Fred [x]  Brian [x]  [Pick another |v]
40065  *
40066  *
40067  *  For this to work: it needs various extra information
40068  *    - normal combo problay has
40069  *      name, hiddenName
40070  *    + displayField, valueField
40071  *
40072  *    For our purpose...
40073  *
40074  *
40075  *   If we change from 'extends' to wrapping...
40076  *   
40077  *  
40078  *
40079  
40080  
40081  * @constructor
40082  * Create a new ComboBoxArray.
40083  * @param {Object} config Configuration options
40084  */
40085  
40086
40087 Roo.form.ComboBoxArray = function(config)
40088 {
40089     
40090     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40091     
40092     this.items = new Roo.util.MixedCollection(false);
40093     
40094     // construct the child combo...
40095     
40096     
40097     
40098     
40099    
40100     
40101 }
40102
40103  
40104 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40105
40106     /**
40107      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40108      */
40109     
40110     lastData : false,
40111     
40112     // behavies liek a hiddne field
40113     inputType:      'hidden',
40114     /**
40115      * @cfg {Number} width The width of the box that displays the selected element
40116      */ 
40117     width:          300,
40118
40119     
40120     
40121     /**
40122      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40123      */
40124     name : false,
40125     /**
40126      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40127      */
40128     hiddenName : false,
40129     
40130     
40131     // private the array of items that are displayed..
40132     items  : false,
40133     // private - the hidden field el.
40134     hiddenEl : false,
40135     // private - the filed el..
40136     el : false,
40137     
40138     //validateValue : function() { return true; }, // all values are ok!
40139     //onAddClick: function() { },
40140     
40141     onRender : function(ct, position) 
40142     {
40143         
40144         // create the standard hidden element
40145         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40146         
40147         
40148         // give fake names to child combo;
40149         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40150         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40151         
40152         this.combo = Roo.factory(this.combo, Roo.form);
40153         this.combo.onRender(ct, position);
40154         if (typeof(this.combo.width) != 'undefined') {
40155             this.combo.onResize(this.combo.width,0);
40156         }
40157         
40158         this.combo.initEvents();
40159         
40160         // assigned so form know we need to do this..
40161         this.store          = this.combo.store;
40162         this.valueField     = this.combo.valueField;
40163         this.displayField   = this.combo.displayField ;
40164         
40165         
40166         this.combo.wrap.addClass('x-cbarray-grp');
40167         
40168         var cbwrap = this.combo.wrap.createChild(
40169             {tag: 'div', cls: 'x-cbarray-cb'},
40170             this.combo.el.dom
40171         );
40172         
40173              
40174         this.hiddenEl = this.combo.wrap.createChild({
40175             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40176         });
40177         this.el = this.combo.wrap.createChild({
40178             tag: 'input',  type:'hidden' , name: this.name, value : ''
40179         });
40180          //   this.el.dom.removeAttribute("name");
40181         
40182         
40183         this.outerWrap = this.combo.wrap;
40184         this.wrap = cbwrap;
40185         
40186         this.outerWrap.setWidth(this.width);
40187         this.outerWrap.dom.removeChild(this.el.dom);
40188         
40189         this.wrap.dom.appendChild(this.el.dom);
40190         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40191         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40192         
40193         this.combo.trigger.setStyle('position','relative');
40194         this.combo.trigger.setStyle('left', '0px');
40195         this.combo.trigger.setStyle('top', '2px');
40196         
40197         this.combo.el.setStyle('vertical-align', 'text-bottom');
40198         
40199         //this.trigger.setStyle('vertical-align', 'top');
40200         
40201         // this should use the code from combo really... on('add' ....)
40202         if (this.adder) {
40203             
40204         
40205             this.adder = this.outerWrap.createChild(
40206                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40207             var _t = this;
40208             this.adder.on('click', function(e) {
40209                 _t.fireEvent('adderclick', this, e);
40210             }, _t);
40211         }
40212         //var _t = this;
40213         //this.adder.on('click', this.onAddClick, _t);
40214         
40215         
40216         this.combo.on('select', function(cb, rec, ix) {
40217             this.addItem(rec.data);
40218             
40219             cb.setValue('');
40220             cb.el.dom.value = '';
40221             //cb.lastData = rec.data;
40222             // add to list
40223             
40224         }, this);
40225         
40226         
40227     },
40228     
40229     
40230     getName: function()
40231     {
40232         // returns hidden if it's set..
40233         if (!this.rendered) {return ''};
40234         return  this.hiddenName ? this.hiddenName : this.name;
40235         
40236     },
40237     
40238     
40239     onResize: function(w, h){
40240         
40241         return;
40242         // not sure if this is needed..
40243         //this.combo.onResize(w,h);
40244         
40245         if(typeof w != 'number'){
40246             // we do not handle it!?!?
40247             return;
40248         }
40249         var tw = this.combo.trigger.getWidth();
40250         tw += this.addicon ? this.addicon.getWidth() : 0;
40251         tw += this.editicon ? this.editicon.getWidth() : 0;
40252         var x = w - tw;
40253         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40254             
40255         this.combo.trigger.setStyle('left', '0px');
40256         
40257         if(this.list && this.listWidth === undefined){
40258             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40259             this.list.setWidth(lw);
40260             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40261         }
40262         
40263     
40264         
40265     },
40266     
40267     addItem: function(rec)
40268     {
40269         var valueField = this.combo.valueField;
40270         var displayField = this.combo.displayField;
40271         if (this.items.indexOfKey(rec[valueField]) > -1) {
40272             //console.log("GOT " + rec.data.id);
40273             return;
40274         }
40275         
40276         var x = new Roo.form.ComboBoxArray.Item({
40277             //id : rec[this.idField],
40278             data : rec,
40279             displayField : displayField ,
40280             tipField : displayField ,
40281             cb : this
40282         });
40283         // use the 
40284         this.items.add(rec[valueField],x);
40285         // add it before the element..
40286         this.updateHiddenEl();
40287         x.render(this.outerWrap, this.wrap.dom);
40288         // add the image handler..
40289     },
40290     
40291     updateHiddenEl : function()
40292     {
40293         this.validate();
40294         if (!this.hiddenEl) {
40295             return;
40296         }
40297         var ar = [];
40298         var idField = this.combo.valueField;
40299         
40300         this.items.each(function(f) {
40301             ar.push(f.data[idField]);
40302            
40303         });
40304         this.hiddenEl.dom.value = ar.join(',');
40305         this.validate();
40306     },
40307     
40308     reset : function()
40309     {
40310         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40311         this.items.each(function(f) {
40312            f.remove(); 
40313         });
40314         this.el.dom.value = '';
40315         if (this.hiddenEl) {
40316             this.hiddenEl.dom.value = '';
40317         }
40318         
40319     },
40320     getValue: function()
40321     {
40322         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40323     },
40324     setValue: function(v) // not a valid action - must use addItems..
40325     {
40326          
40327         this.reset();
40328         
40329         
40330         
40331         if (this.store.isLocal && (typeof(v) == 'string')) {
40332             // then we can use the store to find the values..
40333             // comma seperated at present.. this needs to allow JSON based encoding..
40334             this.hiddenEl.value  = v;
40335             var v_ar = [];
40336             Roo.each(v.split(','), function(k) {
40337                 Roo.log("CHECK " + this.valueField + ',' + k);
40338                 var li = this.store.query(this.valueField, k);
40339                 if (!li.length) {
40340                     return;
40341                 }
40342                 var add = {};
40343                 add[this.valueField] = k;
40344                 add[this.displayField] = li.item(0).data[this.displayField];
40345                 
40346                 this.addItem(add);
40347             }, this) 
40348              
40349         }
40350         if (typeof(v) == 'object') {
40351             // then let's assume it's an array of objects..
40352             Roo.each(v, function(l) {
40353                 this.addItem(l);
40354             }, this);
40355              
40356         }
40357         
40358         
40359     },
40360     setFromData: function(v)
40361     {
40362         // this recieves an object, if setValues is called.
40363         this.reset();
40364         this.el.dom.value = v[this.displayField];
40365         this.hiddenEl.dom.value = v[this.valueField];
40366         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40367             return;
40368         }
40369         var kv = v[this.valueField];
40370         var dv = v[this.displayField];
40371         kv = typeof(kv) != 'string' ? '' : kv;
40372         dv = typeof(dv) != 'string' ? '' : dv;
40373         
40374         
40375         var keys = kv.split(',');
40376         var display = dv.split(',');
40377         for (var i = 0 ; i < keys.length; i++) {
40378             
40379             add = {};
40380             add[this.valueField] = keys[i];
40381             add[this.displayField] = display[i];
40382             this.addItem(add);
40383         }
40384       
40385         
40386     },
40387     
40388     
40389     validateValue : function(value){
40390         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40391         
40392     }
40393     
40394 });
40395
40396
40397
40398 /**
40399  * @class Roo.form.ComboBoxArray.Item
40400  * @extends Roo.BoxComponent
40401  * A selected item in the list
40402  *  Fred [x]  Brian [x]  [Pick another |v]
40403  * 
40404  * @constructor
40405  * Create a new item.
40406  * @param {Object} config Configuration options
40407  */
40408  
40409 Roo.form.ComboBoxArray.Item = function(config) {
40410     config.id = Roo.id();
40411     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40412 }
40413
40414 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40415     data : {},
40416     cb: false,
40417     displayField : false,
40418     tipField : false,
40419     
40420     
40421     defaultAutoCreate : {
40422         tag: 'div',
40423         cls: 'x-cbarray-item',
40424         cn : [ 
40425             { tag: 'div' },
40426             {
40427                 tag: 'img',
40428                 width:16,
40429                 height : 16,
40430                 src : Roo.BLANK_IMAGE_URL ,
40431                 align: 'center'
40432             }
40433         ]
40434         
40435     },
40436     
40437  
40438     onRender : function(ct, position)
40439     {
40440         Roo.form.Field.superclass.onRender.call(this, ct, position);
40441         
40442         if(!this.el){
40443             var cfg = this.getAutoCreate();
40444             this.el = ct.createChild(cfg, position);
40445         }
40446         
40447         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40448         
40449         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40450             this.cb.renderer(this.data) :
40451             String.format('{0}',this.data[this.displayField]);
40452         
40453             
40454         this.el.child('div').dom.setAttribute('qtip',
40455                         String.format('{0}',this.data[this.tipField])
40456         );
40457         
40458         this.el.child('img').on('click', this.remove, this);
40459         
40460     },
40461    
40462     remove : function()
40463     {
40464         
40465         this.cb.items.remove(this);
40466         this.el.child('img').un('click', this.remove, this);
40467         this.el.remove();
40468         this.cb.updateHiddenEl();
40469     }
40470     
40471     
40472 });/*
40473  * Based on:
40474  * Ext JS Library 1.1.1
40475  * Copyright(c) 2006-2007, Ext JS, LLC.
40476  *
40477  * Originally Released Under LGPL - original licence link has changed is not relivant.
40478  *
40479  * Fork - LGPL
40480  * <script type="text/javascript">
40481  */
40482 /**
40483  * @class Roo.form.Checkbox
40484  * @extends Roo.form.Field
40485  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40486  * @constructor
40487  * Creates a new Checkbox
40488  * @param {Object} config Configuration options
40489  */
40490 Roo.form.Checkbox = function(config){
40491     Roo.form.Checkbox.superclass.constructor.call(this, config);
40492     this.addEvents({
40493         /**
40494          * @event check
40495          * Fires when the checkbox is checked or unchecked.
40496              * @param {Roo.form.Checkbox} this This checkbox
40497              * @param {Boolean} checked The new checked value
40498              */
40499         check : true
40500     });
40501 };
40502
40503 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40504     /**
40505      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40506      */
40507     focusClass : undefined,
40508     /**
40509      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40510      */
40511     fieldClass: "x-form-field",
40512     /**
40513      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40514      */
40515     checked: false,
40516     /**
40517      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40518      * {tag: "input", type: "checkbox", autocomplete: "off"})
40519      */
40520     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40521     /**
40522      * @cfg {String} boxLabel The text that appears beside the checkbox
40523      */
40524     boxLabel : "",
40525     /**
40526      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40527      */  
40528     inputValue : '1',
40529     /**
40530      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40531      */
40532      valueOff: '0', // value when not checked..
40533
40534     actionMode : 'viewEl', 
40535     //
40536     // private
40537     itemCls : 'x-menu-check-item x-form-item',
40538     groupClass : 'x-menu-group-item',
40539     inputType : 'hidden',
40540     
40541     
40542     inSetChecked: false, // check that we are not calling self...
40543     
40544     inputElement: false, // real input element?
40545     basedOn: false, // ????
40546     
40547     isFormField: true, // not sure where this is needed!!!!
40548
40549     onResize : function(){
40550         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40551         if(!this.boxLabel){
40552             this.el.alignTo(this.wrap, 'c-c');
40553         }
40554     },
40555
40556     initEvents : function(){
40557         Roo.form.Checkbox.superclass.initEvents.call(this);
40558         this.el.on("click", this.onClick,  this);
40559         this.el.on("change", this.onClick,  this);
40560     },
40561
40562
40563     getResizeEl : function(){
40564         return this.wrap;
40565     },
40566
40567     getPositionEl : function(){
40568         return this.wrap;
40569     },
40570
40571     // private
40572     onRender : function(ct, position){
40573         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40574         /*
40575         if(this.inputValue !== undefined){
40576             this.el.dom.value = this.inputValue;
40577         }
40578         */
40579         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40580         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40581         var viewEl = this.wrap.createChild({ 
40582             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40583         this.viewEl = viewEl;   
40584         this.wrap.on('click', this.onClick,  this); 
40585         
40586         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40587         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40588         
40589         
40590         
40591         if(this.boxLabel){
40592             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40593         //    viewEl.on('click', this.onClick,  this); 
40594         }
40595         //if(this.checked){
40596             this.setChecked(this.checked);
40597         //}else{
40598             //this.checked = this.el.dom;
40599         //}
40600
40601     },
40602
40603     // private
40604     initValue : Roo.emptyFn,
40605
40606     /**
40607      * Returns the checked state of the checkbox.
40608      * @return {Boolean} True if checked, else false
40609      */
40610     getValue : function(){
40611         if(this.el){
40612             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40613         }
40614         return this.valueOff;
40615         
40616     },
40617
40618         // private
40619     onClick : function(){ 
40620         this.setChecked(!this.checked);
40621
40622         //if(this.el.dom.checked != this.checked){
40623         //    this.setValue(this.el.dom.checked);
40624        // }
40625     },
40626
40627     /**
40628      * Sets the checked state of the checkbox.
40629      * On is always based on a string comparison between inputValue and the param.
40630      * @param {Boolean/String} value - the value to set 
40631      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40632      */
40633     setValue : function(v,suppressEvent){
40634         
40635         
40636         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40637         //if(this.el && this.el.dom){
40638         //    this.el.dom.checked = this.checked;
40639         //    this.el.dom.defaultChecked = this.checked;
40640         //}
40641         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40642         //this.fireEvent("check", this, this.checked);
40643     },
40644     // private..
40645     setChecked : function(state,suppressEvent)
40646     {
40647         if (this.inSetChecked) {
40648             this.checked = state;
40649             return;
40650         }
40651         
40652     
40653         if(this.wrap){
40654             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40655         }
40656         this.checked = state;
40657         if(suppressEvent !== true){
40658             this.fireEvent('check', this, state);
40659         }
40660         this.inSetChecked = true;
40661         this.el.dom.value = state ? this.inputValue : this.valueOff;
40662         this.inSetChecked = false;
40663         
40664     },
40665     // handle setting of hidden value by some other method!!?!?
40666     setFromHidden: function()
40667     {
40668         if(!this.el){
40669             return;
40670         }
40671         //console.log("SET FROM HIDDEN");
40672         //alert('setFrom hidden');
40673         this.setValue(this.el.dom.value);
40674     },
40675     
40676     onDestroy : function()
40677     {
40678         if(this.viewEl){
40679             Roo.get(this.viewEl).remove();
40680         }
40681          
40682         Roo.form.Checkbox.superclass.onDestroy.call(this);
40683     }
40684
40685 });/*
40686  * Based on:
40687  * Ext JS Library 1.1.1
40688  * Copyright(c) 2006-2007, Ext JS, LLC.
40689  *
40690  * Originally Released Under LGPL - original licence link has changed is not relivant.
40691  *
40692  * Fork - LGPL
40693  * <script type="text/javascript">
40694  */
40695  
40696 /**
40697  * @class Roo.form.Radio
40698  * @extends Roo.form.Checkbox
40699  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40700  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40701  * @constructor
40702  * Creates a new Radio
40703  * @param {Object} config Configuration options
40704  */
40705 Roo.form.Radio = function(){
40706     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40707 };
40708 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40709     inputType: 'radio',
40710
40711     /**
40712      * If this radio is part of a group, it will return the selected value
40713      * @return {String}
40714      */
40715     getGroupValue : function(){
40716         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40717     },
40718     
40719     
40720     onRender : function(ct, position){
40721         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40722         
40723         if(this.inputValue !== undefined){
40724             this.el.dom.value = this.inputValue;
40725         }
40726          
40727         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40728         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40729         //var viewEl = this.wrap.createChild({ 
40730         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40731         //this.viewEl = viewEl;   
40732         //this.wrap.on('click', this.onClick,  this); 
40733         
40734         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40735         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40736         
40737         
40738         
40739         if(this.boxLabel){
40740             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40741         //    viewEl.on('click', this.onClick,  this); 
40742         }
40743          if(this.checked){
40744             this.el.dom.checked =   'checked' ;
40745         }
40746          
40747     } 
40748     
40749     
40750 });//<script type="text/javascript">
40751
40752 /*
40753  * Ext JS Library 1.1.1
40754  * Copyright(c) 2006-2007, Ext JS, LLC.
40755  * licensing@extjs.com
40756  * 
40757  * http://www.extjs.com/license
40758  */
40759  
40760  /*
40761   * 
40762   * Known bugs:
40763   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40764   * - IE ? - no idea how much works there.
40765   * 
40766   * 
40767   * 
40768   */
40769  
40770
40771 /**
40772  * @class Ext.form.HtmlEditor
40773  * @extends Ext.form.Field
40774  * Provides a lightweight HTML Editor component.
40775  *
40776  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40777  * 
40778  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40779  * supported by this editor.</b><br/><br/>
40780  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40781  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40782  */
40783 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40784       /**
40785      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40786      */
40787     toolbars : false,
40788     /**
40789      * @cfg {String} createLinkText The default text for the create link prompt
40790      */
40791     createLinkText : 'Please enter the URL for the link:',
40792     /**
40793      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40794      */
40795     defaultLinkValue : 'http:/'+'/',
40796    
40797      /**
40798      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40799      *                        Roo.resizable.
40800      */
40801     resizable : false,
40802      /**
40803      * @cfg {Number} height (in pixels)
40804      */   
40805     height: 300,
40806    /**
40807      * @cfg {Number} width (in pixels)
40808      */   
40809     width: 500,
40810     
40811     /**
40812      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40813      * 
40814      */
40815     stylesheets: false,
40816     
40817     // id of frame..
40818     frameId: false,
40819     
40820     // private properties
40821     validationEvent : false,
40822     deferHeight: true,
40823     initialized : false,
40824     activated : false,
40825     sourceEditMode : false,
40826     onFocus : Roo.emptyFn,
40827     iframePad:3,
40828     hideMode:'offsets',
40829     
40830     defaultAutoCreate : { // modified by initCompnoent..
40831         tag: "textarea",
40832         style:"width:500px;height:300px;",
40833         autocomplete: "off"
40834     },
40835
40836     // private
40837     initComponent : function(){
40838         this.addEvents({
40839             /**
40840              * @event initialize
40841              * Fires when the editor is fully initialized (including the iframe)
40842              * @param {HtmlEditor} this
40843              */
40844             initialize: true,
40845             /**
40846              * @event activate
40847              * Fires when the editor is first receives the focus. Any insertion must wait
40848              * until after this event.
40849              * @param {HtmlEditor} this
40850              */
40851             activate: true,
40852              /**
40853              * @event beforesync
40854              * Fires before the textarea is updated with content from the editor iframe. Return false
40855              * to cancel the sync.
40856              * @param {HtmlEditor} this
40857              * @param {String} html
40858              */
40859             beforesync: true,
40860              /**
40861              * @event beforepush
40862              * Fires before the iframe editor is updated with content from the textarea. Return false
40863              * to cancel the push.
40864              * @param {HtmlEditor} this
40865              * @param {String} html
40866              */
40867             beforepush: true,
40868              /**
40869              * @event sync
40870              * Fires when the textarea is updated with content from the editor iframe.
40871              * @param {HtmlEditor} this
40872              * @param {String} html
40873              */
40874             sync: true,
40875              /**
40876              * @event push
40877              * Fires when the iframe editor is updated with content from the textarea.
40878              * @param {HtmlEditor} this
40879              * @param {String} html
40880              */
40881             push: true,
40882              /**
40883              * @event editmodechange
40884              * Fires when the editor switches edit modes
40885              * @param {HtmlEditor} this
40886              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40887              */
40888             editmodechange: true,
40889             /**
40890              * @event editorevent
40891              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40892              * @param {HtmlEditor} this
40893              */
40894             editorevent: true
40895         });
40896         this.defaultAutoCreate =  {
40897             tag: "textarea",
40898             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40899             autocomplete: "off"
40900         };
40901     },
40902
40903     /**
40904      * Protected method that will not generally be called directly. It
40905      * is called when the editor creates its toolbar. Override this method if you need to
40906      * add custom toolbar buttons.
40907      * @param {HtmlEditor} editor
40908      */
40909     createToolbar : function(editor){
40910         if (!editor.toolbars || !editor.toolbars.length) {
40911             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40912         }
40913         
40914         for (var i =0 ; i < editor.toolbars.length;i++) {
40915             editor.toolbars[i] = Roo.factory(
40916                     typeof(editor.toolbars[i]) == 'string' ?
40917                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40918                 Roo.form.HtmlEditor);
40919             editor.toolbars[i].init(editor);
40920         }
40921          
40922         
40923     },
40924
40925     /**
40926      * Protected method that will not generally be called directly. It
40927      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40928      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40929      */
40930     getDocMarkup : function(){
40931         // body styles..
40932         var st = '';
40933         if (this.stylesheets === false) {
40934             
40935             Roo.get(document.head).select('style').each(function(node) {
40936                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40937             });
40938             
40939             Roo.get(document.head).select('link').each(function(node) { 
40940                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40941             });
40942             
40943         } else if (!this.stylesheets.length) {
40944                 // simple..
40945                 st = '<style type="text/css">' +
40946                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40947                    '</style>';
40948         } else {
40949             Roo.each(this.stylesheets, function(s) {
40950                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40951             });
40952             
40953         }
40954         
40955         st +=  '<style type="text/css">' +
40956             'IMG { cursor: pointer } ' +
40957         '</style>';
40958
40959         
40960         return '<html><head>' + st  +
40961             //<style type="text/css">' +
40962             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40963             //'</style>' +
40964             ' </head><body class="roo-htmleditor-body"></body></html>';
40965     },
40966
40967     // private
40968     onRender : function(ct, position)
40969     {
40970         var _t = this;
40971         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40972         this.el.dom.style.border = '0 none';
40973         this.el.dom.setAttribute('tabIndex', -1);
40974         this.el.addClass('x-hidden');
40975         if(Roo.isIE){ // fix IE 1px bogus margin
40976             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40977         }
40978         this.wrap = this.el.wrap({
40979             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40980         });
40981         
40982         if (this.resizable) {
40983             this.resizeEl = new Roo.Resizable(this.wrap, {
40984                 pinned : true,
40985                 wrap: true,
40986                 dynamic : true,
40987                 minHeight : this.height,
40988                 height: this.height,
40989                 handles : this.resizable,
40990                 width: this.width,
40991                 listeners : {
40992                     resize : function(r, w, h) {
40993                         _t.onResize(w,h); // -something
40994                     }
40995                 }
40996             });
40997             
40998         }
40999
41000         this.frameId = Roo.id();
41001         
41002         this.createToolbar(this);
41003         
41004       
41005         
41006         var iframe = this.wrap.createChild({
41007             tag: 'iframe',
41008             id: this.frameId,
41009             name: this.frameId,
41010             frameBorder : 'no',
41011             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41012         }, this.el
41013         );
41014         
41015        // console.log(iframe);
41016         //this.wrap.dom.appendChild(iframe);
41017
41018         this.iframe = iframe.dom;
41019
41020          this.assignDocWin();
41021         
41022         this.doc.designMode = 'on';
41023        
41024         this.doc.open();
41025         this.doc.write(this.getDocMarkup());
41026         this.doc.close();
41027
41028         
41029         var task = { // must defer to wait for browser to be ready
41030             run : function(){
41031                 //console.log("run task?" + this.doc.readyState);
41032                 this.assignDocWin();
41033                 if(this.doc.body || this.doc.readyState == 'complete'){
41034                     try {
41035                         this.doc.designMode="on";
41036                     } catch (e) {
41037                         return;
41038                     }
41039                     Roo.TaskMgr.stop(task);
41040                     this.initEditor.defer(10, this);
41041                 }
41042             },
41043             interval : 10,
41044             duration:10000,
41045             scope: this
41046         };
41047         Roo.TaskMgr.start(task);
41048
41049         if(!this.width){
41050             this.setSize(this.wrap.getSize());
41051         }
41052         if (this.resizeEl) {
41053             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41054             // should trigger onReize..
41055         }
41056     },
41057
41058     // private
41059     onResize : function(w, h)
41060     {
41061         //Roo.log('resize: ' +w + ',' + h );
41062         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41063         if(this.el && this.iframe){
41064             if(typeof w == 'number'){
41065                 var aw = w - this.wrap.getFrameWidth('lr');
41066                 this.el.setWidth(this.adjustWidth('textarea', aw));
41067                 this.iframe.style.width = aw + 'px';
41068             }
41069             if(typeof h == 'number'){
41070                 var tbh = 0;
41071                 for (var i =0; i < this.toolbars.length;i++) {
41072                     // fixme - ask toolbars for heights?
41073                     tbh += this.toolbars[i].tb.el.getHeight();
41074                     if (this.toolbars[i].footer) {
41075                         tbh += this.toolbars[i].footer.el.getHeight();
41076                     }
41077                 }
41078                 
41079                 
41080                 
41081                 
41082                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41083                 ah -= 5; // knock a few pixes off for look..
41084                 this.el.setHeight(this.adjustWidth('textarea', ah));
41085                 this.iframe.style.height = ah + 'px';
41086                 if(this.doc){
41087                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41088                 }
41089             }
41090         }
41091     },
41092
41093     /**
41094      * Toggles the editor between standard and source edit mode.
41095      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41096      */
41097     toggleSourceEdit : function(sourceEditMode){
41098         
41099         this.sourceEditMode = sourceEditMode === true;
41100         
41101         if(this.sourceEditMode){
41102 //            Roo.log('in');
41103 //            Roo.log(this.syncValue());
41104             this.syncValue();
41105             this.iframe.className = 'x-hidden';
41106             this.el.removeClass('x-hidden');
41107             this.el.dom.removeAttribute('tabIndex');
41108             this.el.focus();
41109         }else{
41110 //            Roo.log('out')
41111 //            Roo.log(this.pushValue()); 
41112             this.pushValue();
41113             this.iframe.className = '';
41114             this.el.addClass('x-hidden');
41115             this.el.dom.setAttribute('tabIndex', -1);
41116             this.deferFocus();
41117         }
41118         this.setSize(this.wrap.getSize());
41119         this.fireEvent('editmodechange', this, this.sourceEditMode);
41120     },
41121
41122     // private used internally
41123     createLink : function(){
41124         var url = prompt(this.createLinkText, this.defaultLinkValue);
41125         if(url && url != 'http:/'+'/'){
41126             this.relayCmd('createlink', url);
41127         }
41128     },
41129
41130     // private (for BoxComponent)
41131     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41132
41133     // private (for BoxComponent)
41134     getResizeEl : function(){
41135         return this.wrap;
41136     },
41137
41138     // private (for BoxComponent)
41139     getPositionEl : function(){
41140         return this.wrap;
41141     },
41142
41143     // private
41144     initEvents : function(){
41145         this.originalValue = this.getValue();
41146     },
41147
41148     /**
41149      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41150      * @method
41151      */
41152     markInvalid : Roo.emptyFn,
41153     /**
41154      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41155      * @method
41156      */
41157     clearInvalid : Roo.emptyFn,
41158
41159     setValue : function(v){
41160         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41161         this.pushValue();
41162     },
41163
41164     /**
41165      * Protected method that will not generally be called directly. If you need/want
41166      * custom HTML cleanup, this is the method you should override.
41167      * @param {String} html The HTML to be cleaned
41168      * return {String} The cleaned HTML
41169      */
41170     cleanHtml : function(html){
41171         html = String(html);
41172         if(html.length > 5){
41173             if(Roo.isSafari){ // strip safari nonsense
41174                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41175             }
41176         }
41177         if(html == '&nbsp;'){
41178             html = '';
41179         }
41180         return html;
41181     },
41182
41183     /**
41184      * Protected method that will not generally be called directly. Syncs the contents
41185      * of the editor iframe with the textarea.
41186      */
41187     syncValue : function(){
41188         if(this.initialized){
41189             var bd = (this.doc.body || this.doc.documentElement);
41190             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41191             var html = bd.innerHTML;
41192             if(Roo.isSafari){
41193                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41194                 var m = bs.match(/text-align:(.*?);/i);
41195                 if(m && m[1]){
41196                     html = '<div style="'+m[0]+'">' + html + '</div>';
41197                 }
41198             }
41199             html = this.cleanHtml(html);
41200             // fix up the special chars.. normaly like back quotes in word...
41201             // however we do not want to do this with chinese..
41202             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41203                 var cc = b.charCodeAt();
41204                 if (
41205                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41206                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41207                     (cc >= 0xf900 && cc < 0xfb00 )
41208                 ) {
41209                         return b;
41210                 }
41211                 return "&#"+cc+";" 
41212             });
41213             if(this.fireEvent('beforesync', this, html) !== false){
41214                 this.el.dom.value = html;
41215                 this.fireEvent('sync', this, html);
41216             }
41217         }
41218     },
41219
41220     /**
41221      * Protected method that will not generally be called directly. Pushes the value of the textarea
41222      * into the iframe editor.
41223      */
41224     pushValue : function(){
41225         if(this.initialized){
41226             var v = this.el.dom.value;
41227             
41228             if(v.length < 1){
41229                 v = '&#160;';
41230             }
41231             
41232             if(this.fireEvent('beforepush', this, v) !== false){
41233                 var d = (this.doc.body || this.doc.documentElement);
41234                 d.innerHTML = v;
41235                 this.cleanUpPaste();
41236                 this.el.dom.value = d.innerHTML;
41237                 this.fireEvent('push', this, v);
41238             }
41239         }
41240     },
41241
41242     // private
41243     deferFocus : function(){
41244         this.focus.defer(10, this);
41245     },
41246
41247     // doc'ed in Field
41248     focus : function(){
41249         if(this.win && !this.sourceEditMode){
41250             this.win.focus();
41251         }else{
41252             this.el.focus();
41253         }
41254     },
41255     
41256     assignDocWin: function()
41257     {
41258         var iframe = this.iframe;
41259         
41260          if(Roo.isIE){
41261             this.doc = iframe.contentWindow.document;
41262             this.win = iframe.contentWindow;
41263         } else {
41264             if (!Roo.get(this.frameId)) {
41265                 return;
41266             }
41267             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41268             this.win = Roo.get(this.frameId).dom.contentWindow;
41269         }
41270     },
41271     
41272     // private
41273     initEditor : function(){
41274         //console.log("INIT EDITOR");
41275         this.assignDocWin();
41276         
41277         
41278         
41279         this.doc.designMode="on";
41280         this.doc.open();
41281         this.doc.write(this.getDocMarkup());
41282         this.doc.close();
41283         
41284         var dbody = (this.doc.body || this.doc.documentElement);
41285         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41286         // this copies styles from the containing element into thsi one..
41287         // not sure why we need all of this..
41288         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41289         ss['background-attachment'] = 'fixed'; // w3c
41290         dbody.bgProperties = 'fixed'; // ie
41291         Roo.DomHelper.applyStyles(dbody, ss);
41292         Roo.EventManager.on(this.doc, {
41293             //'mousedown': this.onEditorEvent,
41294             'mouseup': this.onEditorEvent,
41295             'dblclick': this.onEditorEvent,
41296             'click': this.onEditorEvent,
41297             'keyup': this.onEditorEvent,
41298             buffer:100,
41299             scope: this
41300         });
41301         if(Roo.isGecko){
41302             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41303         }
41304         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41305             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41306         }
41307         this.initialized = true;
41308
41309         this.fireEvent('initialize', this);
41310         this.pushValue();
41311     },
41312
41313     // private
41314     onDestroy : function(){
41315         
41316         
41317         
41318         if(this.rendered){
41319             
41320             for (var i =0; i < this.toolbars.length;i++) {
41321                 // fixme - ask toolbars for heights?
41322                 this.toolbars[i].onDestroy();
41323             }
41324             
41325             this.wrap.dom.innerHTML = '';
41326             this.wrap.remove();
41327         }
41328     },
41329
41330     // private
41331     onFirstFocus : function(){
41332         
41333         this.assignDocWin();
41334         
41335         
41336         this.activated = true;
41337         for (var i =0; i < this.toolbars.length;i++) {
41338             this.toolbars[i].onFirstFocus();
41339         }
41340        
41341         if(Roo.isGecko){ // prevent silly gecko errors
41342             this.win.focus();
41343             var s = this.win.getSelection();
41344             if(!s.focusNode || s.focusNode.nodeType != 3){
41345                 var r = s.getRangeAt(0);
41346                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41347                 r.collapse(true);
41348                 this.deferFocus();
41349             }
41350             try{
41351                 this.execCmd('useCSS', true);
41352                 this.execCmd('styleWithCSS', false);
41353             }catch(e){}
41354         }
41355         this.fireEvent('activate', this);
41356     },
41357
41358     // private
41359     adjustFont: function(btn){
41360         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41361         //if(Roo.isSafari){ // safari
41362         //    adjust *= 2;
41363        // }
41364         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41365         if(Roo.isSafari){ // safari
41366             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41367             v =  (v < 10) ? 10 : v;
41368             v =  (v > 48) ? 48 : v;
41369             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41370             
41371         }
41372         
41373         
41374         v = Math.max(1, v+adjust);
41375         
41376         this.execCmd('FontSize', v  );
41377     },
41378
41379     onEditorEvent : function(e){
41380         this.fireEvent('editorevent', this, e);
41381       //  this.updateToolbar();
41382         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41383     },
41384
41385     insertTag : function(tg)
41386     {
41387         // could be a bit smarter... -> wrap the current selected tRoo..
41388         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41389             
41390             range = this.createRange(this.getSelection());
41391             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41392             wrappingNode.appendChild(range.extractContents());
41393             range.insertNode(wrappingNode);
41394
41395             return;
41396             
41397             
41398             
41399         }
41400         this.execCmd("formatblock",   tg);
41401         
41402     },
41403     
41404     insertText : function(txt)
41405     {
41406         
41407         
41408         var range = this.createRange();
41409         range.deleteContents();
41410                //alert(Sender.getAttribute('label'));
41411                
41412         range.insertNode(this.doc.createTextNode(txt));
41413     } ,
41414     
41415     // private
41416     relayBtnCmd : function(btn){
41417         this.relayCmd(btn.cmd);
41418     },
41419
41420     /**
41421      * Executes a Midas editor command on the editor document and performs necessary focus and
41422      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41423      * @param {String} cmd The Midas command
41424      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41425      */
41426     relayCmd : function(cmd, value){
41427         this.win.focus();
41428         this.execCmd(cmd, value);
41429         this.fireEvent('editorevent', this);
41430         //this.updateToolbar();
41431         this.deferFocus();
41432     },
41433
41434     /**
41435      * Executes a Midas editor command directly on the editor document.
41436      * For visual commands, you should use {@link #relayCmd} instead.
41437      * <b>This should only be called after the editor is initialized.</b>
41438      * @param {String} cmd The Midas command
41439      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41440      */
41441     execCmd : function(cmd, value){
41442         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41443         this.syncValue();
41444     },
41445  
41446  
41447    
41448     /**
41449      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41450      * to insert tRoo.
41451      * @param {String} text | dom node.. 
41452      */
41453     insertAtCursor : function(text)
41454     {
41455         
41456         
41457         
41458         if(!this.activated){
41459             return;
41460         }
41461         /*
41462         if(Roo.isIE){
41463             this.win.focus();
41464             var r = this.doc.selection.createRange();
41465             if(r){
41466                 r.collapse(true);
41467                 r.pasteHTML(text);
41468                 this.syncValue();
41469                 this.deferFocus();
41470             
41471             }
41472             return;
41473         }
41474         */
41475         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41476             this.win.focus();
41477             
41478             
41479             // from jquery ui (MIT licenced)
41480             var range, node;
41481             var win = this.win;
41482             
41483             if (win.getSelection && win.getSelection().getRangeAt) {
41484                 range = win.getSelection().getRangeAt(0);
41485                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41486                 range.insertNode(node);
41487             } else if (win.document.selection && win.document.selection.createRange) {
41488                 // no firefox support
41489                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41490                 win.document.selection.createRange().pasteHTML(txt);
41491             } else {
41492                 // no firefox support
41493                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41494                 this.execCmd('InsertHTML', txt);
41495             } 
41496             
41497             this.syncValue();
41498             
41499             this.deferFocus();
41500         }
41501     },
41502  // private
41503     mozKeyPress : function(e){
41504         if(e.ctrlKey){
41505             var c = e.getCharCode(), cmd;
41506           
41507             if(c > 0){
41508                 c = String.fromCharCode(c).toLowerCase();
41509                 switch(c){
41510                     case 'b':
41511                         cmd = 'bold';
41512                         break;
41513                     case 'i':
41514                         cmd = 'italic';
41515                         break;
41516                     
41517                     case 'u':
41518                         cmd = 'underline';
41519                         break;
41520                     
41521                     case 'v':
41522                         this.cleanUpPaste.defer(100, this);
41523                         return;
41524                         
41525                 }
41526                 if(cmd){
41527                     this.win.focus();
41528                     this.execCmd(cmd);
41529                     this.deferFocus();
41530                     e.preventDefault();
41531                 }
41532                 
41533             }
41534         }
41535     },
41536
41537     // private
41538     fixKeys : function(){ // load time branching for fastest keydown performance
41539         if(Roo.isIE){
41540             return function(e){
41541                 var k = e.getKey(), r;
41542                 if(k == e.TAB){
41543                     e.stopEvent();
41544                     r = this.doc.selection.createRange();
41545                     if(r){
41546                         r.collapse(true);
41547                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41548                         this.deferFocus();
41549                     }
41550                     return;
41551                 }
41552                 
41553                 if(k == e.ENTER){
41554                     r = this.doc.selection.createRange();
41555                     if(r){
41556                         var target = r.parentElement();
41557                         if(!target || target.tagName.toLowerCase() != 'li'){
41558                             e.stopEvent();
41559                             r.pasteHTML('<br />');
41560                             r.collapse(false);
41561                             r.select();
41562                         }
41563                     }
41564                 }
41565                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41566                     this.cleanUpPaste.defer(100, this);
41567                     return;
41568                 }
41569                 
41570                 
41571             };
41572         }else if(Roo.isOpera){
41573             return function(e){
41574                 var k = e.getKey();
41575                 if(k == e.TAB){
41576                     e.stopEvent();
41577                     this.win.focus();
41578                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41579                     this.deferFocus();
41580                 }
41581                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41582                     this.cleanUpPaste.defer(100, this);
41583                     return;
41584                 }
41585                 
41586             };
41587         }else if(Roo.isSafari){
41588             return function(e){
41589                 var k = e.getKey();
41590                 
41591                 if(k == e.TAB){
41592                     e.stopEvent();
41593                     this.execCmd('InsertText','\t');
41594                     this.deferFocus();
41595                     return;
41596                 }
41597                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41598                     this.cleanUpPaste.defer(100, this);
41599                     return;
41600                 }
41601                 
41602              };
41603         }
41604     }(),
41605     
41606     getAllAncestors: function()
41607     {
41608         var p = this.getSelectedNode();
41609         var a = [];
41610         if (!p) {
41611             a.push(p); // push blank onto stack..
41612             p = this.getParentElement();
41613         }
41614         
41615         
41616         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41617             a.push(p);
41618             p = p.parentNode;
41619         }
41620         a.push(this.doc.body);
41621         return a;
41622     },
41623     lastSel : false,
41624     lastSelNode : false,
41625     
41626     
41627     getSelection : function() 
41628     {
41629         this.assignDocWin();
41630         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41631     },
41632     
41633     getSelectedNode: function() 
41634     {
41635         // this may only work on Gecko!!!
41636         
41637         // should we cache this!!!!
41638         
41639         
41640         
41641          
41642         var range = this.createRange(this.getSelection()).cloneRange();
41643         
41644         if (Roo.isIE) {
41645             var parent = range.parentElement();
41646             while (true) {
41647                 var testRange = range.duplicate();
41648                 testRange.moveToElementText(parent);
41649                 if (testRange.inRange(range)) {
41650                     break;
41651                 }
41652                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41653                     break;
41654                 }
41655                 parent = parent.parentElement;
41656             }
41657             return parent;
41658         }
41659         
41660         // is ancestor a text element.
41661         var ac =  range.commonAncestorContainer;
41662         if (ac.nodeType == 3) {
41663             ac = ac.parentNode;
41664         }
41665         
41666         var ar = ac.childNodes;
41667          
41668         var nodes = [];
41669         var other_nodes = [];
41670         var has_other_nodes = false;
41671         for (var i=0;i<ar.length;i++) {
41672             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41673                 continue;
41674             }
41675             // fullly contained node.
41676             
41677             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41678                 nodes.push(ar[i]);
41679                 continue;
41680             }
41681             
41682             // probably selected..
41683             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41684                 other_nodes.push(ar[i]);
41685                 continue;
41686             }
41687             // outer..
41688             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41689                 continue;
41690             }
41691             
41692             
41693             has_other_nodes = true;
41694         }
41695         if (!nodes.length && other_nodes.length) {
41696             nodes= other_nodes;
41697         }
41698         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41699             return false;
41700         }
41701         
41702         return nodes[0];
41703     },
41704     createRange: function(sel)
41705     {
41706         // this has strange effects when using with 
41707         // top toolbar - not sure if it's a great idea.
41708         //this.editor.contentWindow.focus();
41709         if (typeof sel != "undefined") {
41710             try {
41711                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41712             } catch(e) {
41713                 return this.doc.createRange();
41714             }
41715         } else {
41716             return this.doc.createRange();
41717         }
41718     },
41719     getParentElement: function()
41720     {
41721         
41722         this.assignDocWin();
41723         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41724         
41725         var range = this.createRange(sel);
41726          
41727         try {
41728             var p = range.commonAncestorContainer;
41729             while (p.nodeType == 3) { // text node
41730                 p = p.parentNode;
41731             }
41732             return p;
41733         } catch (e) {
41734             return null;
41735         }
41736     
41737     },
41738     /***
41739      *
41740      * Range intersection.. the hard stuff...
41741      *  '-1' = before
41742      *  '0' = hits..
41743      *  '1' = after.
41744      *         [ -- selected range --- ]
41745      *   [fail]                        [fail]
41746      *
41747      *    basically..
41748      *      if end is before start or  hits it. fail.
41749      *      if start is after end or hits it fail.
41750      *
41751      *   if either hits (but other is outside. - then it's not 
41752      *   
41753      *    
41754      **/
41755     
41756     
41757     // @see http://www.thismuchiknow.co.uk/?p=64.
41758     rangeIntersectsNode : function(range, node)
41759     {
41760         var nodeRange = node.ownerDocument.createRange();
41761         try {
41762             nodeRange.selectNode(node);
41763         } catch (e) {
41764             nodeRange.selectNodeContents(node);
41765         }
41766     
41767         var rangeStartRange = range.cloneRange();
41768         rangeStartRange.collapse(true);
41769     
41770         var rangeEndRange = range.cloneRange();
41771         rangeEndRange.collapse(false);
41772     
41773         var nodeStartRange = nodeRange.cloneRange();
41774         nodeStartRange.collapse(true);
41775     
41776         var nodeEndRange = nodeRange.cloneRange();
41777         nodeEndRange.collapse(false);
41778     
41779         return rangeStartRange.compareBoundaryPoints(
41780                  Range.START_TO_START, nodeEndRange) == -1 &&
41781                rangeEndRange.compareBoundaryPoints(
41782                  Range.START_TO_START, nodeStartRange) == 1;
41783         
41784          
41785     },
41786     rangeCompareNode : function(range, node)
41787     {
41788         var nodeRange = node.ownerDocument.createRange();
41789         try {
41790             nodeRange.selectNode(node);
41791         } catch (e) {
41792             nodeRange.selectNodeContents(node);
41793         }
41794         
41795         
41796         range.collapse(true);
41797     
41798         nodeRange.collapse(true);
41799      
41800         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41801         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41802          
41803         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41804         
41805         var nodeIsBefore   =  ss == 1;
41806         var nodeIsAfter    = ee == -1;
41807         
41808         if (nodeIsBefore && nodeIsAfter)
41809             return 0; // outer
41810         if (!nodeIsBefore && nodeIsAfter)
41811             return 1; //right trailed.
41812         
41813         if (nodeIsBefore && !nodeIsAfter)
41814             return 2;  // left trailed.
41815         // fully contined.
41816         return 3;
41817     },
41818
41819     // private? - in a new class?
41820     cleanUpPaste :  function()
41821     {
41822         // cleans up the whole document..
41823          Roo.log('cleanuppaste');
41824         this.cleanUpChildren(this.doc.body);
41825         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41826         if (clean != this.doc.body.innerHTML) {
41827             this.doc.body.innerHTML = clean;
41828         }
41829         
41830     },
41831     
41832     cleanWordChars : function(input) {// change the chars to hex code
41833         var he = Roo.form.HtmlEditor;
41834         
41835         var output = input;
41836         Roo.each(he.swapCodes, function(sw) { 
41837             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41838             
41839             output = output.replace(swapper, sw[1]);
41840         });
41841         
41842         return output;
41843     },
41844     
41845     
41846     cleanUpChildren : function (n)
41847     {
41848         if (!n.childNodes.length) {
41849             return;
41850         }
41851         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41852            this.cleanUpChild(n.childNodes[i]);
41853         }
41854     },
41855     
41856     
41857         
41858     
41859     cleanUpChild : function (node)
41860     {
41861         var ed = this;
41862         //console.log(node);
41863         if (node.nodeName == "#text") {
41864             // clean up silly Windows -- stuff?
41865             return; 
41866         }
41867         if (node.nodeName == "#comment") {
41868             node.parentNode.removeChild(node);
41869             // clean up silly Windows -- stuff?
41870             return; 
41871         }
41872         
41873         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41874             // remove node.
41875             node.parentNode.removeChild(node);
41876             return;
41877             
41878         }
41879         
41880         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41881         
41882         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41883         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41884         
41885         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41886         //    remove_keep_children = true;
41887         //}
41888         
41889         if (remove_keep_children) {
41890             this.cleanUpChildren(node);
41891             // inserts everything just before this node...
41892             while (node.childNodes.length) {
41893                 var cn = node.childNodes[0];
41894                 node.removeChild(cn);
41895                 node.parentNode.insertBefore(cn, node);
41896             }
41897             node.parentNode.removeChild(node);
41898             return;
41899         }
41900         
41901         if (!node.attributes || !node.attributes.length) {
41902             this.cleanUpChildren(node);
41903             return;
41904         }
41905         
41906         function cleanAttr(n,v)
41907         {
41908             
41909             if (v.match(/^\./) || v.match(/^\//)) {
41910                 return;
41911             }
41912             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41913                 return;
41914             }
41915             if (v.match(/^#/)) {
41916                 return;
41917             }
41918 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41919             node.removeAttribute(n);
41920             
41921         }
41922         
41923         function cleanStyle(n,v)
41924         {
41925             if (v.match(/expression/)) { //XSS?? should we even bother..
41926                 node.removeAttribute(n);
41927                 return;
41928             }
41929             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41930             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41931             
41932             
41933             var parts = v.split(/;/);
41934             var clean = [];
41935             
41936             Roo.each(parts, function(p) {
41937                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41938                 if (!p.length) {
41939                     return true;
41940                 }
41941                 var l = p.split(':').shift().replace(/\s+/g,'');
41942                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41943                 
41944                 
41945                 if ( cblack.indexOf(l) > -1) {
41946 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41947                     //node.removeAttribute(n);
41948                     return true;
41949                 }
41950                 //Roo.log()
41951                 // only allow 'c whitelisted system attributes'
41952                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41953 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41954                     //node.removeAttribute(n);
41955                     return true;
41956                 }
41957                 
41958                 
41959                  
41960                 
41961                 clean.push(p);
41962                 return true;
41963             });
41964             if (clean.length) { 
41965                 node.setAttribute(n, clean.join(';'));
41966             } else {
41967                 node.removeAttribute(n);
41968             }
41969             
41970         }
41971         
41972         
41973         for (var i = node.attributes.length-1; i > -1 ; i--) {
41974             var a = node.attributes[i];
41975             //console.log(a);
41976             
41977             if (a.name.toLowerCase().substr(0,2)=='on')  {
41978                 node.removeAttribute(a.name);
41979                 continue;
41980             }
41981             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41982                 node.removeAttribute(a.name);
41983                 continue;
41984             }
41985             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41986                 cleanAttr(a.name,a.value); // fixme..
41987                 continue;
41988             }
41989             if (a.name == 'style') {
41990                 cleanStyle(a.name,a.value);
41991                 continue;
41992             }
41993             /// clean up MS crap..
41994             // tecnically this should be a list of valid class'es..
41995             
41996             
41997             if (a.name == 'class') {
41998                 if (a.value.match(/^Mso/)) {
41999                     node.className = '';
42000                 }
42001                 
42002                 if (a.value.match(/body/)) {
42003                     node.className = '';
42004                 }
42005                 continue;
42006             }
42007             
42008             // style cleanup!?
42009             // class cleanup?
42010             
42011         }
42012         
42013         
42014         this.cleanUpChildren(node);
42015         
42016         
42017     }
42018     
42019     
42020     // hide stuff that is not compatible
42021     /**
42022      * @event blur
42023      * @hide
42024      */
42025     /**
42026      * @event change
42027      * @hide
42028      */
42029     /**
42030      * @event focus
42031      * @hide
42032      */
42033     /**
42034      * @event specialkey
42035      * @hide
42036      */
42037     /**
42038      * @cfg {String} fieldClass @hide
42039      */
42040     /**
42041      * @cfg {String} focusClass @hide
42042      */
42043     /**
42044      * @cfg {String} autoCreate @hide
42045      */
42046     /**
42047      * @cfg {String} inputType @hide
42048      */
42049     /**
42050      * @cfg {String} invalidClass @hide
42051      */
42052     /**
42053      * @cfg {String} invalidText @hide
42054      */
42055     /**
42056      * @cfg {String} msgFx @hide
42057      */
42058     /**
42059      * @cfg {String} validateOnBlur @hide
42060      */
42061 });
42062
42063 Roo.form.HtmlEditor.white = [
42064         'area', 'br', 'img', 'input', 'hr', 'wbr',
42065         
42066        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42067        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42068        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42069        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42070        'table',   'ul',         'xmp', 
42071        
42072        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42073       'thead',   'tr', 
42074      
42075       'dir', 'menu', 'ol', 'ul', 'dl',
42076        
42077       'embed',  'object'
42078 ];
42079
42080
42081 Roo.form.HtmlEditor.black = [
42082     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42083         'applet', // 
42084         'base',   'basefont', 'bgsound', 'blink',  'body', 
42085         'frame',  'frameset', 'head',    'html',   'ilayer', 
42086         'iframe', 'layer',  'link',     'meta',    'object',   
42087         'script', 'style' ,'title',  'xml' // clean later..
42088 ];
42089 Roo.form.HtmlEditor.clean = [
42090     'script', 'style', 'title', 'xml'
42091 ];
42092 Roo.form.HtmlEditor.remove = [
42093     'font'
42094 ];
42095 // attributes..
42096
42097 Roo.form.HtmlEditor.ablack = [
42098     'on'
42099 ];
42100     
42101 Roo.form.HtmlEditor.aclean = [ 
42102     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42103 ];
42104
42105 // protocols..
42106 Roo.form.HtmlEditor.pwhite= [
42107         'http',  'https',  'mailto'
42108 ];
42109
42110 // white listed style attributes.
42111 Roo.form.HtmlEditor.cwhite= [
42112       //  'text-align', /// default is to allow most things..
42113       
42114          
42115 //        'font-size'//??
42116 ];
42117
42118 // black listed style attributes.
42119 Roo.form.HtmlEditor.cblack= [
42120       //  'font-size' -- this can be set by the project 
42121 ];
42122
42123
42124 Roo.form.HtmlEditor.swapCodes   =[ 
42125     [    8211, "--" ], 
42126     [    8212, "--" ], 
42127     [    8216,  "'" ],  
42128     [    8217, "'" ],  
42129     [    8220, '"' ],  
42130     [    8221, '"' ],  
42131     [    8226, "*" ],  
42132     [    8230, "..." ]
42133 ]; 
42134
42135     // <script type="text/javascript">
42136 /*
42137  * Based on
42138  * Ext JS Library 1.1.1
42139  * Copyright(c) 2006-2007, Ext JS, LLC.
42140  *  
42141  
42142  */
42143
42144 /**
42145  * @class Roo.form.HtmlEditorToolbar1
42146  * Basic Toolbar
42147  * 
42148  * Usage:
42149  *
42150  new Roo.form.HtmlEditor({
42151     ....
42152     toolbars : [
42153         new Roo.form.HtmlEditorToolbar1({
42154             disable : { fonts: 1 , format: 1, ..., ... , ...],
42155             btns : [ .... ]
42156         })
42157     }
42158      
42159  * 
42160  * @cfg {Object} disable List of elements to disable..
42161  * @cfg {Array} btns List of additional buttons.
42162  * 
42163  * 
42164  * NEEDS Extra CSS? 
42165  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42166  */
42167  
42168 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42169 {
42170     
42171     Roo.apply(this, config);
42172     
42173     // default disabled, based on 'good practice'..
42174     this.disable = this.disable || {};
42175     Roo.applyIf(this.disable, {
42176         fontSize : true,
42177         colors : true,
42178         specialElements : true
42179     });
42180     
42181     
42182     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42183     // dont call parent... till later.
42184 }
42185
42186 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42187     
42188     tb: false,
42189     
42190     rendered: false,
42191     
42192     editor : false,
42193     /**
42194      * @cfg {Object} disable  List of toolbar elements to disable
42195          
42196      */
42197     disable : false,
42198       /**
42199      * @cfg {Array} fontFamilies An array of available font families
42200      */
42201     fontFamilies : [
42202         'Arial',
42203         'Courier New',
42204         'Tahoma',
42205         'Times New Roman',
42206         'Verdana'
42207     ],
42208     
42209     specialChars : [
42210            "&#169;",
42211           "&#174;",     
42212           "&#8482;",    
42213           "&#163;" ,    
42214          // "&#8212;",    
42215           "&#8230;",    
42216           "&#247;" ,    
42217         //  "&#225;" ,     ?? a acute?
42218            "&#8364;"    , //Euro
42219        //   "&#8220;"    ,
42220         //  "&#8221;"    ,
42221         //  "&#8226;"    ,
42222           "&#176;"  //   , // degrees
42223
42224          // "&#233;"     , // e ecute
42225          // "&#250;"     , // u ecute?
42226     ],
42227     
42228     specialElements : [
42229         {
42230             text: "Insert Table",
42231             xtype: 'MenuItem',
42232             xns : Roo.Menu,
42233             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42234                 
42235         },
42236         {    
42237             text: "Insert Image",
42238             xtype: 'MenuItem',
42239             xns : Roo.Menu,
42240             ihtml : '<img src="about:blank"/>'
42241             
42242         }
42243         
42244          
42245     ],
42246     
42247     
42248     inputElements : [ 
42249             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42250             "input:submit", "input:button", "select", "textarea", "label" ],
42251     formats : [
42252         ["p"] ,  
42253         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42254         ["pre"],[ "code"], 
42255         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42256         ['div'],['span']
42257     ],
42258     
42259     cleanStyles : [
42260         "font-size"
42261     ],
42262      /**
42263      * @cfg {String} defaultFont default font to use.
42264      */
42265     defaultFont: 'tahoma',
42266    
42267     fontSelect : false,
42268     
42269     
42270     formatCombo : false,
42271     
42272     init : function(editor)
42273     {
42274         this.editor = editor;
42275         
42276         
42277         var fid = editor.frameId;
42278         var etb = this;
42279         function btn(id, toggle, handler){
42280             var xid = fid + '-'+ id ;
42281             return {
42282                 id : xid,
42283                 cmd : id,
42284                 cls : 'x-btn-icon x-edit-'+id,
42285                 enableToggle:toggle !== false,
42286                 scope: editor, // was editor...
42287                 handler:handler||editor.relayBtnCmd,
42288                 clickEvent:'mousedown',
42289                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42290                 tabIndex:-1
42291             };
42292         }
42293         
42294         
42295         
42296         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42297         this.tb = tb;
42298          // stop form submits
42299         tb.el.on('click', function(e){
42300             e.preventDefault(); // what does this do?
42301         });
42302
42303         if(!this.disable.font) { // && !Roo.isSafari){
42304             /* why no safari for fonts 
42305             editor.fontSelect = tb.el.createChild({
42306                 tag:'select',
42307                 tabIndex: -1,
42308                 cls:'x-font-select',
42309                 html: this.createFontOptions()
42310             });
42311             
42312             editor.fontSelect.on('change', function(){
42313                 var font = editor.fontSelect.dom.value;
42314                 editor.relayCmd('fontname', font);
42315                 editor.deferFocus();
42316             }, editor);
42317             
42318             tb.add(
42319                 editor.fontSelect.dom,
42320                 '-'
42321             );
42322             */
42323             
42324         };
42325         if(!this.disable.formats){
42326             this.formatCombo = new Roo.form.ComboBox({
42327                 store: new Roo.data.SimpleStore({
42328                     id : 'tag',
42329                     fields: ['tag'],
42330                     data : this.formats // from states.js
42331                 }),
42332                 blockFocus : true,
42333                 name : '',
42334                 //autoCreate : {tag: "div",  size: "20"},
42335                 displayField:'tag',
42336                 typeAhead: false,
42337                 mode: 'local',
42338                 editable : false,
42339                 triggerAction: 'all',
42340                 emptyText:'Add tag',
42341                 selectOnFocus:true,
42342                 width:135,
42343                 listeners : {
42344                     'select': function(c, r, i) {
42345                         editor.insertTag(r.get('tag'));
42346                         editor.focus();
42347                     }
42348                 }
42349
42350             });
42351             tb.addField(this.formatCombo);
42352             
42353         }
42354         
42355         if(!this.disable.format){
42356             tb.add(
42357                 btn('bold'),
42358                 btn('italic'),
42359                 btn('underline')
42360             );
42361         };
42362         if(!this.disable.fontSize){
42363             tb.add(
42364                 '-',
42365                 
42366                 
42367                 btn('increasefontsize', false, editor.adjustFont),
42368                 btn('decreasefontsize', false, editor.adjustFont)
42369             );
42370         };
42371         
42372         
42373         if(!this.disable.colors){
42374             tb.add(
42375                 '-', {
42376                     id:editor.frameId +'-forecolor',
42377                     cls:'x-btn-icon x-edit-forecolor',
42378                     clickEvent:'mousedown',
42379                     tooltip: this.buttonTips['forecolor'] || undefined,
42380                     tabIndex:-1,
42381                     menu : new Roo.menu.ColorMenu({
42382                         allowReselect: true,
42383                         focus: Roo.emptyFn,
42384                         value:'000000',
42385                         plain:true,
42386                         selectHandler: function(cp, color){
42387                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42388                             editor.deferFocus();
42389                         },
42390                         scope: editor,
42391                         clickEvent:'mousedown'
42392                     })
42393                 }, {
42394                     id:editor.frameId +'backcolor',
42395                     cls:'x-btn-icon x-edit-backcolor',
42396                     clickEvent:'mousedown',
42397                     tooltip: this.buttonTips['backcolor'] || undefined,
42398                     tabIndex:-1,
42399                     menu : new Roo.menu.ColorMenu({
42400                         focus: Roo.emptyFn,
42401                         value:'FFFFFF',
42402                         plain:true,
42403                         allowReselect: true,
42404                         selectHandler: function(cp, color){
42405                             if(Roo.isGecko){
42406                                 editor.execCmd('useCSS', false);
42407                                 editor.execCmd('hilitecolor', color);
42408                                 editor.execCmd('useCSS', true);
42409                                 editor.deferFocus();
42410                             }else{
42411                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42412                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42413                                 editor.deferFocus();
42414                             }
42415                         },
42416                         scope:editor,
42417                         clickEvent:'mousedown'
42418                     })
42419                 }
42420             );
42421         };
42422         // now add all the items...
42423         
42424
42425         if(!this.disable.alignments){
42426             tb.add(
42427                 '-',
42428                 btn('justifyleft'),
42429                 btn('justifycenter'),
42430                 btn('justifyright')
42431             );
42432         };
42433
42434         //if(!Roo.isSafari){
42435             if(!this.disable.links){
42436                 tb.add(
42437                     '-',
42438                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42439                 );
42440             };
42441
42442             if(!this.disable.lists){
42443                 tb.add(
42444                     '-',
42445                     btn('insertorderedlist'),
42446                     btn('insertunorderedlist')
42447                 );
42448             }
42449             if(!this.disable.sourceEdit){
42450                 tb.add(
42451                     '-',
42452                     btn('sourceedit', true, function(btn){
42453                         this.toggleSourceEdit(btn.pressed);
42454                     })
42455                 );
42456             }
42457         //}
42458         
42459         var smenu = { };
42460         // special menu.. - needs to be tidied up..
42461         if (!this.disable.special) {
42462             smenu = {
42463                 text: "&#169;",
42464                 cls: 'x-edit-none',
42465                 
42466                 menu : {
42467                     items : []
42468                 }
42469             };
42470             for (var i =0; i < this.specialChars.length; i++) {
42471                 smenu.menu.items.push({
42472                     
42473                     html: this.specialChars[i],
42474                     handler: function(a,b) {
42475                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42476                         //editor.insertAtCursor(a.html);
42477                         
42478                     },
42479                     tabIndex:-1
42480                 });
42481             }
42482             
42483             
42484             tb.add(smenu);
42485             
42486             
42487         }
42488         
42489         var cmenu = { };
42490         if (!this.disable.cleanStyles) {
42491             cmenu = {
42492                 cls: 'x-btn-icon x-btn-clear',
42493                 
42494                 menu : {
42495                     items : []
42496                 }
42497             };
42498             for (var i =0; i < this.cleanStyles.length; i++) {
42499                 cmenu.menu.items.push({
42500                     actiontype : this.cleanStyles[i],
42501                     html: 'Remove ' + this.cleanStyles[i],
42502                     handler: function(a,b) {
42503                         Roo.log(a);
42504                         Roo.log(b);
42505                         var c = Roo.get(editor.doc.body);
42506                         c.select('[style]').each(function(s) {
42507                             s.dom.style.removeProperty(a.actiontype);
42508                         });
42509                         
42510                     },
42511                     tabIndex:-1
42512                 });
42513             }
42514             
42515             tb.add(cmenu);
42516         }
42517          
42518         if (!this.disable.specialElements) {
42519             var semenu = {
42520                 text: "Other;",
42521                 cls: 'x-edit-none',
42522                 menu : {
42523                     items : []
42524                 }
42525             };
42526             for (var i =0; i < this.specialElements.length; i++) {
42527                 semenu.menu.items.push(
42528                     Roo.apply({ 
42529                         handler: function(a,b) {
42530                             editor.insertAtCursor(this.ihtml);
42531                         }
42532                     }, this.specialElements[i])
42533                 );
42534                     
42535             }
42536             
42537             tb.add(semenu);
42538             
42539             
42540         }
42541          
42542         
42543         if (this.btns) {
42544             for(var i =0; i< this.btns.length;i++) {
42545                 var b = Roo.factory(this.btns[i],Roo.form);
42546                 b.cls =  'x-edit-none';
42547                 b.scope = editor;
42548                 tb.add(b);
42549             }
42550         
42551         }
42552         
42553         
42554         
42555         // disable everything...
42556         
42557         this.tb.items.each(function(item){
42558            if(item.id != editor.frameId+ '-sourceedit'){
42559                 item.disable();
42560             }
42561         });
42562         this.rendered = true;
42563         
42564         // the all the btns;
42565         editor.on('editorevent', this.updateToolbar, this);
42566         // other toolbars need to implement this..
42567         //editor.on('editmodechange', this.updateToolbar, this);
42568     },
42569     
42570     
42571     
42572     /**
42573      * Protected method that will not generally be called directly. It triggers
42574      * a toolbar update by reading the markup state of the current selection in the editor.
42575      */
42576     updateToolbar: function(){
42577
42578         if(!this.editor.activated){
42579             this.editor.onFirstFocus();
42580             return;
42581         }
42582
42583         var btns = this.tb.items.map, 
42584             doc = this.editor.doc,
42585             frameId = this.editor.frameId;
42586
42587         if(!this.disable.font && !Roo.isSafari){
42588             /*
42589             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42590             if(name != this.fontSelect.dom.value){
42591                 this.fontSelect.dom.value = name;
42592             }
42593             */
42594         }
42595         if(!this.disable.format){
42596             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42597             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42598             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42599         }
42600         if(!this.disable.alignments){
42601             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42602             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42603             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42604         }
42605         if(!Roo.isSafari && !this.disable.lists){
42606             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42607             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42608         }
42609         
42610         var ans = this.editor.getAllAncestors();
42611         if (this.formatCombo) {
42612             
42613             
42614             var store = this.formatCombo.store;
42615             this.formatCombo.setValue("");
42616             for (var i =0; i < ans.length;i++) {
42617                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42618                     // select it..
42619                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42620                     break;
42621                 }
42622             }
42623         }
42624         
42625         
42626         
42627         // hides menus... - so this cant be on a menu...
42628         Roo.menu.MenuMgr.hideAll();
42629
42630         //this.editorsyncValue();
42631     },
42632    
42633     
42634     createFontOptions : function(){
42635         var buf = [], fs = this.fontFamilies, ff, lc;
42636         
42637         
42638         
42639         for(var i = 0, len = fs.length; i< len; i++){
42640             ff = fs[i];
42641             lc = ff.toLowerCase();
42642             buf.push(
42643                 '<option value="',lc,'" style="font-family:',ff,';"',
42644                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42645                     ff,
42646                 '</option>'
42647             );
42648         }
42649         return buf.join('');
42650     },
42651     
42652     toggleSourceEdit : function(sourceEditMode){
42653         if(sourceEditMode === undefined){
42654             sourceEditMode = !this.sourceEditMode;
42655         }
42656         this.sourceEditMode = sourceEditMode === true;
42657         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42658         // just toggle the button?
42659         if(btn.pressed !== this.editor.sourceEditMode){
42660             btn.toggle(this.editor.sourceEditMode);
42661             return;
42662         }
42663         
42664         if(this.sourceEditMode){
42665             this.tb.items.each(function(item){
42666                 if(item.cmd != 'sourceedit'){
42667                     item.disable();
42668                 }
42669             });
42670           
42671         }else{
42672             if(this.initialized){
42673                 this.tb.items.each(function(item){
42674                     item.enable();
42675                 });
42676             }
42677             
42678         }
42679         // tell the editor that it's been pressed..
42680         this.editor.toggleSourceEdit(sourceEditMode);
42681        
42682     },
42683      /**
42684      * Object collection of toolbar tooltips for the buttons in the editor. The key
42685      * is the command id associated with that button and the value is a valid QuickTips object.
42686      * For example:
42687 <pre><code>
42688 {
42689     bold : {
42690         title: 'Bold (Ctrl+B)',
42691         text: 'Make the selected text bold.',
42692         cls: 'x-html-editor-tip'
42693     },
42694     italic : {
42695         title: 'Italic (Ctrl+I)',
42696         text: 'Make the selected text italic.',
42697         cls: 'x-html-editor-tip'
42698     },
42699     ...
42700 </code></pre>
42701     * @type Object
42702      */
42703     buttonTips : {
42704         bold : {
42705             title: 'Bold (Ctrl+B)',
42706             text: 'Make the selected text bold.',
42707             cls: 'x-html-editor-tip'
42708         },
42709         italic : {
42710             title: 'Italic (Ctrl+I)',
42711             text: 'Make the selected text italic.',
42712             cls: 'x-html-editor-tip'
42713         },
42714         underline : {
42715             title: 'Underline (Ctrl+U)',
42716             text: 'Underline the selected text.',
42717             cls: 'x-html-editor-tip'
42718         },
42719         increasefontsize : {
42720             title: 'Grow Text',
42721             text: 'Increase the font size.',
42722             cls: 'x-html-editor-tip'
42723         },
42724         decreasefontsize : {
42725             title: 'Shrink Text',
42726             text: 'Decrease the font size.',
42727             cls: 'x-html-editor-tip'
42728         },
42729         backcolor : {
42730             title: 'Text Highlight Color',
42731             text: 'Change the background color of the selected text.',
42732             cls: 'x-html-editor-tip'
42733         },
42734         forecolor : {
42735             title: 'Font Color',
42736             text: 'Change the color of the selected text.',
42737             cls: 'x-html-editor-tip'
42738         },
42739         justifyleft : {
42740             title: 'Align Text Left',
42741             text: 'Align text to the left.',
42742             cls: 'x-html-editor-tip'
42743         },
42744         justifycenter : {
42745             title: 'Center Text',
42746             text: 'Center text in the editor.',
42747             cls: 'x-html-editor-tip'
42748         },
42749         justifyright : {
42750             title: 'Align Text Right',
42751             text: 'Align text to the right.',
42752             cls: 'x-html-editor-tip'
42753         },
42754         insertunorderedlist : {
42755             title: 'Bullet List',
42756             text: 'Start a bulleted list.',
42757             cls: 'x-html-editor-tip'
42758         },
42759         insertorderedlist : {
42760             title: 'Numbered List',
42761             text: 'Start a numbered list.',
42762             cls: 'x-html-editor-tip'
42763         },
42764         createlink : {
42765             title: 'Hyperlink',
42766             text: 'Make the selected text a hyperlink.',
42767             cls: 'x-html-editor-tip'
42768         },
42769         sourceedit : {
42770             title: 'Source Edit',
42771             text: 'Switch to source editing mode.',
42772             cls: 'x-html-editor-tip'
42773         }
42774     },
42775     // private
42776     onDestroy : function(){
42777         if(this.rendered){
42778             
42779             this.tb.items.each(function(item){
42780                 if(item.menu){
42781                     item.menu.removeAll();
42782                     if(item.menu.el){
42783                         item.menu.el.destroy();
42784                     }
42785                 }
42786                 item.destroy();
42787             });
42788              
42789         }
42790     },
42791     onFirstFocus: function() {
42792         this.tb.items.each(function(item){
42793            item.enable();
42794         });
42795     }
42796 });
42797
42798
42799
42800
42801 // <script type="text/javascript">
42802 /*
42803  * Based on
42804  * Ext JS Library 1.1.1
42805  * Copyright(c) 2006-2007, Ext JS, LLC.
42806  *  
42807  
42808  */
42809
42810  
42811 /**
42812  * @class Roo.form.HtmlEditor.ToolbarContext
42813  * Context Toolbar
42814  * 
42815  * Usage:
42816  *
42817  new Roo.form.HtmlEditor({
42818     ....
42819     toolbars : [
42820         { xtype: 'ToolbarStandard', styles : {} }
42821         { xtype: 'ToolbarContext', disable : {} }
42822     ]
42823 })
42824
42825      
42826  * 
42827  * @config : {Object} disable List of elements to disable.. (not done yet.)
42828  * @config : {Object} styles  Map of styles available.
42829  * 
42830  */
42831
42832 Roo.form.HtmlEditor.ToolbarContext = function(config)
42833 {
42834     
42835     Roo.apply(this, config);
42836     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42837     // dont call parent... till later.
42838     this.styles = this.styles || {};
42839 }
42840
42841  
42842
42843 Roo.form.HtmlEditor.ToolbarContext.types = {
42844     'IMG' : {
42845         width : {
42846             title: "Width",
42847             width: 40
42848         },
42849         height:  {
42850             title: "Height",
42851             width: 40
42852         },
42853         align: {
42854             title: "Align",
42855             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42856             width : 80
42857             
42858         },
42859         border: {
42860             title: "Border",
42861             width: 40
42862         },
42863         alt: {
42864             title: "Alt",
42865             width: 120
42866         },
42867         src : {
42868             title: "Src",
42869             width: 220
42870         }
42871         
42872     },
42873     'A' : {
42874         name : {
42875             title: "Name",
42876             width: 50
42877         },
42878         target:  {
42879             title: "Target",
42880             width: 120
42881         },
42882         href:  {
42883             title: "Href",
42884             width: 220
42885         } // border?
42886         
42887     },
42888     'TABLE' : {
42889         rows : {
42890             title: "Rows",
42891             width: 20
42892         },
42893         cols : {
42894             title: "Cols",
42895             width: 20
42896         },
42897         width : {
42898             title: "Width",
42899             width: 40
42900         },
42901         height : {
42902             title: "Height",
42903             width: 40
42904         },
42905         border : {
42906             title: "Border",
42907             width: 20
42908         }
42909     },
42910     'TD' : {
42911         width : {
42912             title: "Width",
42913             width: 40
42914         },
42915         height : {
42916             title: "Height",
42917             width: 40
42918         },   
42919         align: {
42920             title: "Align",
42921             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42922             width: 80
42923         },
42924         valign: {
42925             title: "Valign",
42926             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42927             width: 80
42928         },
42929         colspan: {
42930             title: "Colspan",
42931             width: 20
42932             
42933         },
42934          'font-family'  : {
42935             title : "Font",
42936             style : 'fontFamily',
42937             displayField: 'display',
42938             optname : 'font-family',
42939             width: 140
42940         }
42941     },
42942     'INPUT' : {
42943         name : {
42944             title: "name",
42945             width: 120
42946         },
42947         value : {
42948             title: "Value",
42949             width: 120
42950         },
42951         width : {
42952             title: "Width",
42953             width: 40
42954         }
42955     },
42956     'LABEL' : {
42957         'for' : {
42958             title: "For",
42959             width: 120
42960         }
42961     },
42962     'TEXTAREA' : {
42963           name : {
42964             title: "name",
42965             width: 120
42966         },
42967         rows : {
42968             title: "Rows",
42969             width: 20
42970         },
42971         cols : {
42972             title: "Cols",
42973             width: 20
42974         }
42975     },
42976     'SELECT' : {
42977         name : {
42978             title: "name",
42979             width: 120
42980         },
42981         selectoptions : {
42982             title: "Options",
42983             width: 200
42984         }
42985     },
42986     
42987     // should we really allow this??
42988     // should this just be 
42989     'BODY' : {
42990         title : {
42991             title: "Title",
42992             width: 200,
42993             disabled : true
42994         }
42995     },
42996     'SPAN' : {
42997         'font-family'  : {
42998             title : "Font",
42999             style : 'fontFamily',
43000             displayField: 'display',
43001             optname : 'font-family',
43002             width: 140
43003         }
43004     },
43005     'DIV' : {
43006         'font-family'  : {
43007             title : "Font",
43008             style : 'fontFamily',
43009             displayField: 'display',
43010             optname : 'font-family',
43011             width: 140
43012         }
43013     },
43014      'P' : {
43015         'font-family'  : {
43016             title : "Font",
43017             style : 'fontFamily',
43018             displayField: 'display',
43019             optname : 'font-family',
43020             width: 140
43021         }
43022     },
43023     
43024     '*' : {
43025         // empty..
43026     }
43027
43028 };
43029
43030 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43031 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43032
43033 Roo.form.HtmlEditor.ToolbarContext.options = {
43034         'font-family'  : [ 
43035                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43036                 [ 'Courier New', 'Courier New'],
43037                 [ 'Tahoma', 'Tahoma'],
43038                 [ 'Times New Roman,serif', 'Times'],
43039                 [ 'Verdana','Verdana' ]
43040         ]
43041 };
43042
43043 // fixme - these need to be configurable..
43044  
43045
43046 Roo.form.HtmlEditor.ToolbarContext.types
43047
43048
43049 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43050     
43051     tb: false,
43052     
43053     rendered: false,
43054     
43055     editor : false,
43056     /**
43057      * @cfg {Object} disable  List of toolbar elements to disable
43058          
43059      */
43060     disable : false,
43061     /**
43062      * @cfg {Object} styles List of styles 
43063      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43064      *
43065      * These must be defined in the page, so they get rendered correctly..
43066      * .headline { }
43067      * TD.underline { }
43068      * 
43069      */
43070     styles : false,
43071     
43072     options: false,
43073     
43074     toolbars : false,
43075     
43076     init : function(editor)
43077     {
43078         this.editor = editor;
43079         
43080         
43081         var fid = editor.frameId;
43082         var etb = this;
43083         function btn(id, toggle, handler){
43084             var xid = fid + '-'+ id ;
43085             return {
43086                 id : xid,
43087                 cmd : id,
43088                 cls : 'x-btn-icon x-edit-'+id,
43089                 enableToggle:toggle !== false,
43090                 scope: editor, // was editor...
43091                 handler:handler||editor.relayBtnCmd,
43092                 clickEvent:'mousedown',
43093                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43094                 tabIndex:-1
43095             };
43096         }
43097         // create a new element.
43098         var wdiv = editor.wrap.createChild({
43099                 tag: 'div'
43100             }, editor.wrap.dom.firstChild.nextSibling, true);
43101         
43102         // can we do this more than once??
43103         
43104          // stop form submits
43105       
43106  
43107         // disable everything...
43108         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43109         this.toolbars = {};
43110            
43111         for (var i in  ty) {
43112           
43113             this.toolbars[i] = this.buildToolbar(ty[i],i);
43114         }
43115         this.tb = this.toolbars.BODY;
43116         this.tb.el.show();
43117         this.buildFooter();
43118         this.footer.show();
43119         editor.on('hide', function( ) { this.footer.hide() }, this);
43120         editor.on('show', function( ) { this.footer.show() }, this);
43121         
43122          
43123         this.rendered = true;
43124         
43125         // the all the btns;
43126         editor.on('editorevent', this.updateToolbar, this);
43127         // other toolbars need to implement this..
43128         //editor.on('editmodechange', this.updateToolbar, this);
43129     },
43130     
43131     
43132     
43133     /**
43134      * Protected method that will not generally be called directly. It triggers
43135      * a toolbar update by reading the markup state of the current selection in the editor.
43136      */
43137     updateToolbar: function(editor,ev,sel){
43138
43139         //Roo.log(ev);
43140         // capture mouse up - this is handy for selecting images..
43141         // perhaps should go somewhere else...
43142         if(!this.editor.activated){
43143              this.editor.onFirstFocus();
43144             return;
43145         }
43146         
43147         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43148         // selectNode - might want to handle IE?
43149         if (ev &&
43150             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43151             ev.target && ev.target.tagName == 'IMG') {
43152             // they have click on an image...
43153             // let's see if we can change the selection...
43154             sel = ev.target;
43155          
43156               var nodeRange = sel.ownerDocument.createRange();
43157             try {
43158                 nodeRange.selectNode(sel);
43159             } catch (e) {
43160                 nodeRange.selectNodeContents(sel);
43161             }
43162             //nodeRange.collapse(true);
43163             var s = editor.win.getSelection();
43164             s.removeAllRanges();
43165             s.addRange(nodeRange);
43166         }  
43167         
43168       
43169         var updateFooter = sel ? false : true;
43170         
43171         
43172         var ans = this.editor.getAllAncestors();
43173         
43174         // pick
43175         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43176         
43177         if (!sel) { 
43178             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43179             sel = sel ? sel : this.editor.doc.body;
43180             sel = sel.tagName.length ? sel : this.editor.doc.body;
43181             
43182         }
43183         // pick a menu that exists..
43184         var tn = sel.tagName.toUpperCase();
43185         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43186         
43187         tn = sel.tagName.toUpperCase();
43188         
43189         var lastSel = this.tb.selectedNode
43190         
43191         this.tb.selectedNode = sel;
43192         
43193         // if current menu does not match..
43194         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43195                 
43196             this.tb.el.hide();
43197             ///console.log("show: " + tn);
43198             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43199             this.tb.el.show();
43200             // update name
43201             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43202             
43203             
43204             // update attributes
43205             if (this.tb.fields) {
43206                 this.tb.fields.each(function(e) {
43207                     if (e.stylename) {
43208                         e.setValue(sel.style[e.stylename]);
43209                         return;
43210                     } 
43211                    e.setValue(sel.getAttribute(e.attrname));
43212                 });
43213             }
43214             
43215             var hasStyles = false;
43216             for(var i in this.styles) {
43217                 hasStyles = true;
43218                 break;
43219             }
43220             
43221             // update styles
43222             if (hasStyles) { 
43223                 var st = this.tb.fields.item(0);
43224                 
43225                 st.store.removeAll();
43226                
43227                 
43228                 var cn = sel.className.split(/\s+/);
43229                 
43230                 var avs = [];
43231                 if (this.styles['*']) {
43232                     
43233                     Roo.each(this.styles['*'], function(v) {
43234                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43235                     });
43236                 }
43237                 if (this.styles[tn]) { 
43238                     Roo.each(this.styles[tn], function(v) {
43239                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43240                     });
43241                 }
43242                 
43243                 st.store.loadData(avs);
43244                 st.collapse();
43245                 st.setValue(cn);
43246             }
43247             // flag our selected Node.
43248             this.tb.selectedNode = sel;
43249            
43250            
43251             Roo.menu.MenuMgr.hideAll();
43252
43253         }
43254         
43255         if (!updateFooter) {
43256             //this.footDisp.dom.innerHTML = ''; 
43257             return;
43258         }
43259         // update the footer
43260         //
43261         var html = '';
43262         
43263         this.footerEls = ans.reverse();
43264         Roo.each(this.footerEls, function(a,i) {
43265             if (!a) { return; }
43266             html += html.length ? ' &gt; '  :  '';
43267             
43268             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43269             
43270         });
43271        
43272         // 
43273         var sz = this.footDisp.up('td').getSize();
43274         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43275         this.footDisp.dom.style.marginLeft = '5px';
43276         
43277         this.footDisp.dom.style.overflow = 'hidden';
43278         
43279         this.footDisp.dom.innerHTML = html;
43280             
43281         //this.editorsyncValue();
43282     },
43283      
43284     
43285    
43286        
43287     // private
43288     onDestroy : function(){
43289         if(this.rendered){
43290             
43291             this.tb.items.each(function(item){
43292                 if(item.menu){
43293                     item.menu.removeAll();
43294                     if(item.menu.el){
43295                         item.menu.el.destroy();
43296                     }
43297                 }
43298                 item.destroy();
43299             });
43300              
43301         }
43302     },
43303     onFirstFocus: function() {
43304         // need to do this for all the toolbars..
43305         this.tb.items.each(function(item){
43306            item.enable();
43307         });
43308     },
43309     buildToolbar: function(tlist, nm)
43310     {
43311         var editor = this.editor;
43312          // create a new element.
43313         var wdiv = editor.wrap.createChild({
43314                 tag: 'div'
43315             }, editor.wrap.dom.firstChild.nextSibling, true);
43316         
43317        
43318         var tb = new Roo.Toolbar(wdiv);
43319         // add the name..
43320         
43321         tb.add(nm+ ":&nbsp;");
43322         
43323         var styles = [];
43324         for(var i in this.styles) {
43325             styles.push(i);
43326         }
43327         
43328         // styles...
43329         if (styles && styles.length) {
43330             
43331             // this needs a multi-select checkbox...
43332             tb.addField( new Roo.form.ComboBox({
43333                 store: new Roo.data.SimpleStore({
43334                     id : 'val',
43335                     fields: ['val', 'selected'],
43336                     data : [] 
43337                 }),
43338                 name : '-roo-edit-className',
43339                 attrname : 'className',
43340                 displayField: 'val',
43341                 typeAhead: false,
43342                 mode: 'local',
43343                 editable : false,
43344                 triggerAction: 'all',
43345                 emptyText:'Select Style',
43346                 selectOnFocus:true,
43347                 width: 130,
43348                 listeners : {
43349                     'select': function(c, r, i) {
43350                         // initial support only for on class per el..
43351                         tb.selectedNode.className =  r ? r.get('val') : '';
43352                         editor.syncValue();
43353                     }
43354                 }
43355     
43356             }));
43357         }
43358         
43359         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43360         var tbops = tbc.options;
43361         
43362         for (var i in tlist) {
43363             
43364             var item = tlist[i];
43365             tb.add(item.title + ":&nbsp;");
43366             
43367             
43368             //optname == used so you can configure the options available..
43369             var opts = item.opts ? item.opts : false;
43370             if (item.optname) {
43371                 opts = tbops[item.optname];
43372            
43373             }
43374             
43375             if (opts) {
43376                 // opts == pulldown..
43377                 tb.addField( new Roo.form.ComboBox({
43378                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43379                         id : 'val',
43380                         fields: ['val', 'display'],
43381                         data : opts  
43382                     }),
43383                     name : '-roo-edit-' + i,
43384                     attrname : i,
43385                     stylename : item.style ? item.style : false,
43386                     displayField: item.displayField ? item.displayField : 'val',
43387                     valueField :  'val',
43388                     typeAhead: false,
43389                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43390                     editable : false,
43391                     triggerAction: 'all',
43392                     emptyText:'Select',
43393                     selectOnFocus:true,
43394                     width: item.width ? item.width  : 130,
43395                     listeners : {
43396                         'select': function(c, r, i) {
43397                             if (c.stylename) {
43398                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43399                                 return;
43400                             }
43401                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43402                         }
43403                     }
43404
43405                 }));
43406                 continue;
43407                     
43408                  
43409                 
43410                 tb.addField( new Roo.form.TextField({
43411                     name: i,
43412                     width: 100,
43413                     //allowBlank:false,
43414                     value: ''
43415                 }));
43416                 continue;
43417             }
43418             tb.addField( new Roo.form.TextField({
43419                 name: '-roo-edit-' + i,
43420                 attrname : i,
43421                 
43422                 width: item.width,
43423                 //allowBlank:true,
43424                 value: '',
43425                 listeners: {
43426                     'change' : function(f, nv, ov) {
43427                         tb.selectedNode.setAttribute(f.attrname, nv);
43428                     }
43429                 }
43430             }));
43431              
43432         }
43433         tb.addFill();
43434         var _this = this;
43435         tb.addButton( {
43436             text: 'Remove Tag',
43437     
43438             listeners : {
43439                 click : function ()
43440                 {
43441                     // remove
43442                     // undo does not work.
43443                      
43444                     var sn = tb.selectedNode;
43445                     
43446                     var pn = sn.parentNode;
43447                     
43448                     var stn =  sn.childNodes[0];
43449                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43450                     while (sn.childNodes.length) {
43451                         var node = sn.childNodes[0];
43452                         sn.removeChild(node);
43453                         //Roo.log(node);
43454                         pn.insertBefore(node, sn);
43455                         
43456                     }
43457                     pn.removeChild(sn);
43458                     var range = editor.createRange();
43459         
43460                     range.setStart(stn,0);
43461                     range.setEnd(en,0); //????
43462                     //range.selectNode(sel);
43463                     
43464                     
43465                     var selection = editor.getSelection();
43466                     selection.removeAllRanges();
43467                     selection.addRange(range);
43468                     
43469                     
43470                     
43471                     //_this.updateToolbar(null, null, pn);
43472                     _this.updateToolbar(null, null, null);
43473                     _this.footDisp.dom.innerHTML = ''; 
43474                 }
43475             }
43476             
43477                     
43478                 
43479             
43480         });
43481         
43482         
43483         tb.el.on('click', function(e){
43484             e.preventDefault(); // what does this do?
43485         });
43486         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43487         tb.el.hide();
43488         tb.name = nm;
43489         // dont need to disable them... as they will get hidden
43490         return tb;
43491          
43492         
43493     },
43494     buildFooter : function()
43495     {
43496         
43497         var fel = this.editor.wrap.createChild();
43498         this.footer = new Roo.Toolbar(fel);
43499         // toolbar has scrolly on left / right?
43500         var footDisp= new Roo.Toolbar.Fill();
43501         var _t = this;
43502         this.footer.add(
43503             {
43504                 text : '&lt;',
43505                 xtype: 'Button',
43506                 handler : function() {
43507                     _t.footDisp.scrollTo('left',0,true)
43508                 }
43509             }
43510         );
43511         this.footer.add( footDisp );
43512         this.footer.add( 
43513             {
43514                 text : '&gt;',
43515                 xtype: 'Button',
43516                 handler : function() {
43517                     // no animation..
43518                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43519                 }
43520             }
43521         );
43522         var fel = Roo.get(footDisp.el);
43523         fel.addClass('x-editor-context');
43524         this.footDispWrap = fel; 
43525         this.footDispWrap.overflow  = 'hidden';
43526         
43527         this.footDisp = fel.createChild();
43528         this.footDispWrap.on('click', this.onContextClick, this)
43529         
43530         
43531     },
43532     onContextClick : function (ev,dom)
43533     {
43534         ev.preventDefault();
43535         var  cn = dom.className;
43536         //Roo.log(cn);
43537         if (!cn.match(/x-ed-loc-/)) {
43538             return;
43539         }
43540         var n = cn.split('-').pop();
43541         var ans = this.footerEls;
43542         var sel = ans[n];
43543         
43544          // pick
43545         var range = this.editor.createRange();
43546         
43547         range.selectNodeContents(sel);
43548         //range.selectNode(sel);
43549         
43550         
43551         var selection = this.editor.getSelection();
43552         selection.removeAllRanges();
43553         selection.addRange(range);
43554         
43555         
43556         
43557         this.updateToolbar(null, null, sel);
43558         
43559         
43560     }
43561     
43562     
43563     
43564     
43565     
43566 });
43567
43568
43569
43570
43571
43572 /*
43573  * Based on:
43574  * Ext JS Library 1.1.1
43575  * Copyright(c) 2006-2007, Ext JS, LLC.
43576  *
43577  * Originally Released Under LGPL - original licence link has changed is not relivant.
43578  *
43579  * Fork - LGPL
43580  * <script type="text/javascript">
43581  */
43582  
43583 /**
43584  * @class Roo.form.BasicForm
43585  * @extends Roo.util.Observable
43586  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43587  * @constructor
43588  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43589  * @param {Object} config Configuration options
43590  */
43591 Roo.form.BasicForm = function(el, config){
43592     this.allItems = [];
43593     this.childForms = [];
43594     Roo.apply(this, config);
43595     /*
43596      * The Roo.form.Field items in this form.
43597      * @type MixedCollection
43598      */
43599      
43600      
43601     this.items = new Roo.util.MixedCollection(false, function(o){
43602         return o.id || (o.id = Roo.id());
43603     });
43604     this.addEvents({
43605         /**
43606          * @event beforeaction
43607          * Fires before any action is performed. Return false to cancel the action.
43608          * @param {Form} this
43609          * @param {Action} action The action to be performed
43610          */
43611         beforeaction: true,
43612         /**
43613          * @event actionfailed
43614          * Fires when an action fails.
43615          * @param {Form} this
43616          * @param {Action} action The action that failed
43617          */
43618         actionfailed : true,
43619         /**
43620          * @event actioncomplete
43621          * Fires when an action is completed.
43622          * @param {Form} this
43623          * @param {Action} action The action that completed
43624          */
43625         actioncomplete : true
43626     });
43627     if(el){
43628         this.initEl(el);
43629     }
43630     Roo.form.BasicForm.superclass.constructor.call(this);
43631 };
43632
43633 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43634     /**
43635      * @cfg {String} method
43636      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43637      */
43638     /**
43639      * @cfg {DataReader} reader
43640      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43641      * This is optional as there is built-in support for processing JSON.
43642      */
43643     /**
43644      * @cfg {DataReader} errorReader
43645      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43646      * This is completely optional as there is built-in support for processing JSON.
43647      */
43648     /**
43649      * @cfg {String} url
43650      * The URL to use for form actions if one isn't supplied in the action options.
43651      */
43652     /**
43653      * @cfg {Boolean} fileUpload
43654      * Set to true if this form is a file upload.
43655      */
43656      
43657     /**
43658      * @cfg {Object} baseParams
43659      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43660      */
43661      /**
43662      
43663     /**
43664      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43665      */
43666     timeout: 30,
43667
43668     // private
43669     activeAction : null,
43670
43671     /**
43672      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43673      * or setValues() data instead of when the form was first created.
43674      */
43675     trackResetOnLoad : false,
43676     
43677     
43678     /**
43679      * childForms - used for multi-tab forms
43680      * @type {Array}
43681      */
43682     childForms : false,
43683     
43684     /**
43685      * allItems - full list of fields.
43686      * @type {Array}
43687      */
43688     allItems : false,
43689     
43690     /**
43691      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43692      * element by passing it or its id or mask the form itself by passing in true.
43693      * @type Mixed
43694      */
43695     waitMsgTarget : false,
43696
43697     // private
43698     initEl : function(el){
43699         this.el = Roo.get(el);
43700         this.id = this.el.id || Roo.id();
43701         this.el.on('submit', this.onSubmit, this);
43702         this.el.addClass('x-form');
43703     },
43704
43705     // private
43706     onSubmit : function(e){
43707         e.stopEvent();
43708     },
43709
43710     /**
43711      * Returns true if client-side validation on the form is successful.
43712      * @return Boolean
43713      */
43714     isValid : function(){
43715         var valid = true;
43716         this.items.each(function(f){
43717            if(!f.validate()){
43718                valid = false;
43719            }
43720         });
43721         return valid;
43722     },
43723
43724     /**
43725      * Returns true if any fields in this form have changed since their original load.
43726      * @return Boolean
43727      */
43728     isDirty : function(){
43729         var dirty = false;
43730         this.items.each(function(f){
43731            if(f.isDirty()){
43732                dirty = true;
43733                return false;
43734            }
43735         });
43736         return dirty;
43737     },
43738
43739     /**
43740      * Performs a predefined action (submit or load) or custom actions you define on this form.
43741      * @param {String} actionName The name of the action type
43742      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43743      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43744      * accept other config options):
43745      * <pre>
43746 Property          Type             Description
43747 ----------------  ---------------  ----------------------------------------------------------------------------------
43748 url               String           The url for the action (defaults to the form's url)
43749 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43750 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43751 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43752                                    validate the form on the client (defaults to false)
43753      * </pre>
43754      * @return {BasicForm} this
43755      */
43756     doAction : function(action, options){
43757         if(typeof action == 'string'){
43758             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43759         }
43760         if(this.fireEvent('beforeaction', this, action) !== false){
43761             this.beforeAction(action);
43762             action.run.defer(100, action);
43763         }
43764         return this;
43765     },
43766
43767     /**
43768      * Shortcut to do a submit action.
43769      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43770      * @return {BasicForm} this
43771      */
43772     submit : function(options){
43773         this.doAction('submit', options);
43774         return this;
43775     },
43776
43777     /**
43778      * Shortcut to do a load action.
43779      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43780      * @return {BasicForm} this
43781      */
43782     load : function(options){
43783         this.doAction('load', options);
43784         return this;
43785     },
43786
43787     /**
43788      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43789      * @param {Record} record The record to edit
43790      * @return {BasicForm} this
43791      */
43792     updateRecord : function(record){
43793         record.beginEdit();
43794         var fs = record.fields;
43795         fs.each(function(f){
43796             var field = this.findField(f.name);
43797             if(field){
43798                 record.set(f.name, field.getValue());
43799             }
43800         }, this);
43801         record.endEdit();
43802         return this;
43803     },
43804
43805     /**
43806      * Loads an Roo.data.Record into this form.
43807      * @param {Record} record The record to load
43808      * @return {BasicForm} this
43809      */
43810     loadRecord : function(record){
43811         this.setValues(record.data);
43812         return this;
43813     },
43814
43815     // private
43816     beforeAction : function(action){
43817         var o = action.options;
43818         
43819        
43820         if(this.waitMsgTarget === true){
43821             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43822         }else if(this.waitMsgTarget){
43823             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43824             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43825         }else {
43826             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43827         }
43828          
43829     },
43830
43831     // private
43832     afterAction : function(action, success){
43833         this.activeAction = null;
43834         var o = action.options;
43835         
43836         if(this.waitMsgTarget === true){
43837             this.el.unmask();
43838         }else if(this.waitMsgTarget){
43839             this.waitMsgTarget.unmask();
43840         }else{
43841             Roo.MessageBox.updateProgress(1);
43842             Roo.MessageBox.hide();
43843         }
43844          
43845         if(success){
43846             if(o.reset){
43847                 this.reset();
43848             }
43849             Roo.callback(o.success, o.scope, [this, action]);
43850             this.fireEvent('actioncomplete', this, action);
43851             
43852         }else{
43853             
43854             // failure condition..
43855             // we have a scenario where updates need confirming.
43856             // eg. if a locking scenario exists..
43857             // we look for { errors : { needs_confirm : true }} in the response.
43858             if (
43859                 (typeof(action.result) != 'undefined')  &&
43860                 (typeof(action.result.errors) != 'undefined')  &&
43861                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43862            ){
43863                 var _t = this;
43864                 Roo.MessageBox.confirm(
43865                     "Change requires confirmation",
43866                     action.result.errorMsg,
43867                     function(r) {
43868                         if (r != 'yes') {
43869                             return;
43870                         }
43871                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43872                     }
43873                     
43874                 );
43875                 
43876                 
43877                 
43878                 return;
43879             }
43880             
43881             Roo.callback(o.failure, o.scope, [this, action]);
43882             // show an error message if no failed handler is set..
43883             if (!this.hasListener('actionfailed')) {
43884                 Roo.MessageBox.alert("Error",
43885                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43886                         action.result.errorMsg :
43887                         "Saving Failed, please check your entries or try again"
43888                 );
43889             }
43890             
43891             this.fireEvent('actionfailed', this, action);
43892         }
43893         
43894     },
43895
43896     /**
43897      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43898      * @param {String} id The value to search for
43899      * @return Field
43900      */
43901     findField : function(id){
43902         var field = this.items.get(id);
43903         if(!field){
43904             this.items.each(function(f){
43905                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43906                     field = f;
43907                     return false;
43908                 }
43909             });
43910         }
43911         return field || null;
43912     },
43913
43914     /**
43915      * Add a secondary form to this one, 
43916      * Used to provide tabbed forms. One form is primary, with hidden values 
43917      * which mirror the elements from the other forms.
43918      * 
43919      * @param {Roo.form.Form} form to add.
43920      * 
43921      */
43922     addForm : function(form)
43923     {
43924        
43925         if (this.childForms.indexOf(form) > -1) {
43926             // already added..
43927             return;
43928         }
43929         this.childForms.push(form);
43930         var n = '';
43931         Roo.each(form.allItems, function (fe) {
43932             
43933             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43934             if (this.findField(n)) { // already added..
43935                 return;
43936             }
43937             var add = new Roo.form.Hidden({
43938                 name : n
43939             });
43940             add.render(this.el);
43941             
43942             this.add( add );
43943         }, this);
43944         
43945     },
43946     /**
43947      * Mark fields in this form invalid in bulk.
43948      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43949      * @return {BasicForm} this
43950      */
43951     markInvalid : function(errors){
43952         if(errors instanceof Array){
43953             for(var i = 0, len = errors.length; i < len; i++){
43954                 var fieldError = errors[i];
43955                 var f = this.findField(fieldError.id);
43956                 if(f){
43957                     f.markInvalid(fieldError.msg);
43958                 }
43959             }
43960         }else{
43961             var field, id;
43962             for(id in errors){
43963                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43964                     field.markInvalid(errors[id]);
43965                 }
43966             }
43967         }
43968         Roo.each(this.childForms || [], function (f) {
43969             f.markInvalid(errors);
43970         });
43971         
43972         return this;
43973     },
43974
43975     /**
43976      * Set values for fields in this form in bulk.
43977      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43978      * @return {BasicForm} this
43979      */
43980     setValues : function(values){
43981         if(values instanceof Array){ // array of objects
43982             for(var i = 0, len = values.length; i < len; i++){
43983                 var v = values[i];
43984                 var f = this.findField(v.id);
43985                 if(f){
43986                     f.setValue(v.value);
43987                     if(this.trackResetOnLoad){
43988                         f.originalValue = f.getValue();
43989                     }
43990                 }
43991             }
43992         }else{ // object hash
43993             var field, id;
43994             for(id in values){
43995                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43996                     
43997                     if (field.setFromData && 
43998                         field.valueField && 
43999                         field.displayField &&
44000                         // combos' with local stores can 
44001                         // be queried via setValue()
44002                         // to set their value..
44003                         (field.store && !field.store.isLocal)
44004                         ) {
44005                         // it's a combo
44006                         var sd = { };
44007                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44008                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44009                         field.setFromData(sd);
44010                         
44011                     } else {
44012                         field.setValue(values[id]);
44013                     }
44014                     
44015                     
44016                     if(this.trackResetOnLoad){
44017                         field.originalValue = field.getValue();
44018                     }
44019                 }
44020             }
44021         }
44022          
44023         Roo.each(this.childForms || [], function (f) {
44024             f.setValues(values);
44025         });
44026                 
44027         return this;
44028     },
44029
44030     /**
44031      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44032      * they are returned as an array.
44033      * @param {Boolean} asString
44034      * @return {Object}
44035      */
44036     getValues : function(asString){
44037         if (this.childForms) {
44038             // copy values from the child forms
44039             Roo.each(this.childForms, function (f) {
44040                 this.setValues(f.getValues());
44041             }, this);
44042         }
44043         
44044         
44045         
44046         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44047         if(asString === true){
44048             return fs;
44049         }
44050         return Roo.urlDecode(fs);
44051     },
44052     
44053     /**
44054      * Returns the fields in this form as an object with key/value pairs. 
44055      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44056      * @return {Object}
44057      */
44058     getFieldValues : function(with_hidden)
44059     {
44060         if (this.childForms) {
44061             // copy values from the child forms
44062             // should this call getFieldValues - probably not as we do not currently copy
44063             // hidden fields when we generate..
44064             Roo.each(this.childForms, function (f) {
44065                 this.setValues(f.getValues());
44066             }, this);
44067         }
44068         
44069         var ret = {};
44070         this.items.each(function(f){
44071             if (!f.getName()) {
44072                 return;
44073             }
44074             var v = f.getValue();
44075             if (f.inputType =='radio') {
44076                 if (typeof(ret[f.getName()]) == 'undefined') {
44077                     ret[f.getName()] = ''; // empty..
44078                 }
44079                 
44080                 if (!f.el.dom.checked) {
44081                     return;
44082                     
44083                 }
44084                 v = f.el.dom.value;
44085                 
44086             }
44087             
44088             // not sure if this supported any more..
44089             if ((typeof(v) == 'object') && f.getRawValue) {
44090                 v = f.getRawValue() ; // dates..
44091             }
44092             // combo boxes where name != hiddenName...
44093             if (f.name != f.getName()) {
44094                 ret[f.name] = f.getRawValue();
44095             }
44096             ret[f.getName()] = v;
44097         });
44098         
44099         return ret;
44100     },
44101
44102     /**
44103      * Clears all invalid messages in this form.
44104      * @return {BasicForm} this
44105      */
44106     clearInvalid : function(){
44107         this.items.each(function(f){
44108            f.clearInvalid();
44109         });
44110         
44111         Roo.each(this.childForms || [], function (f) {
44112             f.clearInvalid();
44113         });
44114         
44115         
44116         return this;
44117     },
44118
44119     /**
44120      * Resets this form.
44121      * @return {BasicForm} this
44122      */
44123     reset : function(){
44124         this.items.each(function(f){
44125             f.reset();
44126         });
44127         
44128         Roo.each(this.childForms || [], function (f) {
44129             f.reset();
44130         });
44131        
44132         
44133         return this;
44134     },
44135
44136     /**
44137      * Add Roo.form components to this form.
44138      * @param {Field} field1
44139      * @param {Field} field2 (optional)
44140      * @param {Field} etc (optional)
44141      * @return {BasicForm} this
44142      */
44143     add : function(){
44144         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44145         return this;
44146     },
44147
44148
44149     /**
44150      * Removes a field from the items collection (does NOT remove its markup).
44151      * @param {Field} field
44152      * @return {BasicForm} this
44153      */
44154     remove : function(field){
44155         this.items.remove(field);
44156         return this;
44157     },
44158
44159     /**
44160      * Looks at the fields in this form, checks them for an id attribute,
44161      * and calls applyTo on the existing dom element with that id.
44162      * @return {BasicForm} this
44163      */
44164     render : function(){
44165         this.items.each(function(f){
44166             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44167                 f.applyTo(f.id);
44168             }
44169         });
44170         return this;
44171     },
44172
44173     /**
44174      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44175      * @param {Object} values
44176      * @return {BasicForm} this
44177      */
44178     applyToFields : function(o){
44179         this.items.each(function(f){
44180            Roo.apply(f, o);
44181         });
44182         return this;
44183     },
44184
44185     /**
44186      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44187      * @param {Object} values
44188      * @return {BasicForm} this
44189      */
44190     applyIfToFields : function(o){
44191         this.items.each(function(f){
44192            Roo.applyIf(f, o);
44193         });
44194         return this;
44195     }
44196 });
44197
44198 // back compat
44199 Roo.BasicForm = Roo.form.BasicForm;/*
44200  * Based on:
44201  * Ext JS Library 1.1.1
44202  * Copyright(c) 2006-2007, Ext JS, LLC.
44203  *
44204  * Originally Released Under LGPL - original licence link has changed is not relivant.
44205  *
44206  * Fork - LGPL
44207  * <script type="text/javascript">
44208  */
44209
44210 /**
44211  * @class Roo.form.Form
44212  * @extends Roo.form.BasicForm
44213  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44214  * @constructor
44215  * @param {Object} config Configuration options
44216  */
44217 Roo.form.Form = function(config){
44218     var xitems =  [];
44219     if (config.items) {
44220         xitems = config.items;
44221         delete config.items;
44222     }
44223    
44224     
44225     Roo.form.Form.superclass.constructor.call(this, null, config);
44226     this.url = this.url || this.action;
44227     if(!this.root){
44228         this.root = new Roo.form.Layout(Roo.applyIf({
44229             id: Roo.id()
44230         }, config));
44231     }
44232     this.active = this.root;
44233     /**
44234      * Array of all the buttons that have been added to this form via {@link addButton}
44235      * @type Array
44236      */
44237     this.buttons = [];
44238     this.allItems = [];
44239     this.addEvents({
44240         /**
44241          * @event clientvalidation
44242          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44243          * @param {Form} this
44244          * @param {Boolean} valid true if the form has passed client-side validation
44245          */
44246         clientvalidation: true,
44247         /**
44248          * @event rendered
44249          * Fires when the form is rendered
44250          * @param {Roo.form.Form} form
44251          */
44252         rendered : true
44253     });
44254     
44255     if (this.progressUrl) {
44256             // push a hidden field onto the list of fields..
44257             this.addxtype( {
44258                     xns: Roo.form, 
44259                     xtype : 'Hidden', 
44260                     name : 'UPLOAD_IDENTIFIER' 
44261             });
44262         }
44263         
44264     
44265     Roo.each(xitems, this.addxtype, this);
44266     
44267     
44268     
44269 };
44270
44271 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44272     /**
44273      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44274      */
44275     /**
44276      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44277      */
44278     /**
44279      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44280      */
44281     buttonAlign:'center',
44282
44283     /**
44284      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44285      */
44286     minButtonWidth:75,
44287
44288     /**
44289      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44290      * This property cascades to child containers if not set.
44291      */
44292     labelAlign:'left',
44293
44294     /**
44295      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44296      * fires a looping event with that state. This is required to bind buttons to the valid
44297      * state using the config value formBind:true on the button.
44298      */
44299     monitorValid : false,
44300
44301     /**
44302      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44303      */
44304     monitorPoll : 200,
44305     
44306     /**
44307      * @cfg {String} progressUrl - Url to return progress data 
44308      */
44309     
44310     progressUrl : false,
44311   
44312     /**
44313      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44314      * fields are added and the column is closed. If no fields are passed the column remains open
44315      * until end() is called.
44316      * @param {Object} config The config to pass to the column
44317      * @param {Field} field1 (optional)
44318      * @param {Field} field2 (optional)
44319      * @param {Field} etc (optional)
44320      * @return Column The column container object
44321      */
44322     column : function(c){
44323         var col = new Roo.form.Column(c);
44324         this.start(col);
44325         if(arguments.length > 1){ // duplicate code required because of Opera
44326             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44327             this.end();
44328         }
44329         return col;
44330     },
44331
44332     /**
44333      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44334      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44335      * until end() is called.
44336      * @param {Object} config The config to pass to the fieldset
44337      * @param {Field} field1 (optional)
44338      * @param {Field} field2 (optional)
44339      * @param {Field} etc (optional)
44340      * @return FieldSet The fieldset container object
44341      */
44342     fieldset : function(c){
44343         var fs = new Roo.form.FieldSet(c);
44344         this.start(fs);
44345         if(arguments.length > 1){ // duplicate code required because of Opera
44346             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44347             this.end();
44348         }
44349         return fs;
44350     },
44351
44352     /**
44353      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44354      * fields are added and the container is closed. If no fields are passed the container remains open
44355      * until end() is called.
44356      * @param {Object} config The config to pass to the Layout
44357      * @param {Field} field1 (optional)
44358      * @param {Field} field2 (optional)
44359      * @param {Field} etc (optional)
44360      * @return Layout The container object
44361      */
44362     container : function(c){
44363         var l = new Roo.form.Layout(c);
44364         this.start(l);
44365         if(arguments.length > 1){ // duplicate code required because of Opera
44366             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44367             this.end();
44368         }
44369         return l;
44370     },
44371
44372     /**
44373      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44374      * @param {Object} container A Roo.form.Layout or subclass of Layout
44375      * @return {Form} this
44376      */
44377     start : function(c){
44378         // cascade label info
44379         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44380         this.active.stack.push(c);
44381         c.ownerCt = this.active;
44382         this.active = c;
44383         return this;
44384     },
44385
44386     /**
44387      * Closes the current open container
44388      * @return {Form} this
44389      */
44390     end : function(){
44391         if(this.active == this.root){
44392             return this;
44393         }
44394         this.active = this.active.ownerCt;
44395         return this;
44396     },
44397
44398     /**
44399      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44400      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44401      * as the label of the field.
44402      * @param {Field} field1
44403      * @param {Field} field2 (optional)
44404      * @param {Field} etc. (optional)
44405      * @return {Form} this
44406      */
44407     add : function(){
44408         this.active.stack.push.apply(this.active.stack, arguments);
44409         this.allItems.push.apply(this.allItems,arguments);
44410         var r = [];
44411         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44412             if(a[i].isFormField){
44413                 r.push(a[i]);
44414             }
44415         }
44416         if(r.length > 0){
44417             Roo.form.Form.superclass.add.apply(this, r);
44418         }
44419         return this;
44420     },
44421     
44422
44423     
44424     
44425     
44426      /**
44427      * Find any element that has been added to a form, using it's ID or name
44428      * This can include framesets, columns etc. along with regular fields..
44429      * @param {String} id - id or name to find.
44430      
44431      * @return {Element} e - or false if nothing found.
44432      */
44433     findbyId : function(id)
44434     {
44435         var ret = false;
44436         if (!id) {
44437             return ret;
44438         }
44439         Roo.each(this.allItems, function(f){
44440             if (f.id == id || f.name == id ){
44441                 ret = f;
44442                 return false;
44443             }
44444         });
44445         return ret;
44446     },
44447
44448     
44449     
44450     /**
44451      * Render this form into the passed container. This should only be called once!
44452      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44453      * @return {Form} this
44454      */
44455     render : function(ct)
44456     {
44457         
44458         
44459         
44460         ct = Roo.get(ct);
44461         var o = this.autoCreate || {
44462             tag: 'form',
44463             method : this.method || 'POST',
44464             id : this.id || Roo.id()
44465         };
44466         this.initEl(ct.createChild(o));
44467
44468         this.root.render(this.el);
44469         
44470        
44471              
44472         this.items.each(function(f){
44473             f.render('x-form-el-'+f.id);
44474         });
44475
44476         if(this.buttons.length > 0){
44477             // tables are required to maintain order and for correct IE layout
44478             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44479                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44480                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44481             }}, null, true);
44482             var tr = tb.getElementsByTagName('tr')[0];
44483             for(var i = 0, len = this.buttons.length; i < len; i++) {
44484                 var b = this.buttons[i];
44485                 var td = document.createElement('td');
44486                 td.className = 'x-form-btn-td';
44487                 b.render(tr.appendChild(td));
44488             }
44489         }
44490         if(this.monitorValid){ // initialize after render
44491             this.startMonitoring();
44492         }
44493         this.fireEvent('rendered', this);
44494         return this;
44495     },
44496
44497     /**
44498      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44499      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44500      * object or a valid Roo.DomHelper element config
44501      * @param {Function} handler The function called when the button is clicked
44502      * @param {Object} scope (optional) The scope of the handler function
44503      * @return {Roo.Button}
44504      */
44505     addButton : function(config, handler, scope){
44506         var bc = {
44507             handler: handler,
44508             scope: scope,
44509             minWidth: this.minButtonWidth,
44510             hideParent:true
44511         };
44512         if(typeof config == "string"){
44513             bc.text = config;
44514         }else{
44515             Roo.apply(bc, config);
44516         }
44517         var btn = new Roo.Button(null, bc);
44518         this.buttons.push(btn);
44519         return btn;
44520     },
44521
44522      /**
44523      * Adds a series of form elements (using the xtype property as the factory method.
44524      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44525      * @param {Object} config 
44526      */
44527     
44528     addxtype : function()
44529     {
44530         var ar = Array.prototype.slice.call(arguments, 0);
44531         var ret = false;
44532         for(var i = 0; i < ar.length; i++) {
44533             if (!ar[i]) {
44534                 continue; // skip -- if this happends something invalid got sent, we 
44535                 // should ignore it, as basically that interface element will not show up
44536                 // and that should be pretty obvious!!
44537             }
44538             
44539             if (Roo.form[ar[i].xtype]) {
44540                 ar[i].form = this;
44541                 var fe = Roo.factory(ar[i], Roo.form);
44542                 if (!ret) {
44543                     ret = fe;
44544                 }
44545                 fe.form = this;
44546                 if (fe.store) {
44547                     fe.store.form = this;
44548                 }
44549                 if (fe.isLayout) {  
44550                          
44551                     this.start(fe);
44552                     this.allItems.push(fe);
44553                     if (fe.items && fe.addxtype) {
44554                         fe.addxtype.apply(fe, fe.items);
44555                         delete fe.items;
44556                     }
44557                      this.end();
44558                     continue;
44559                 }
44560                 
44561                 
44562                  
44563                 this.add(fe);
44564               //  console.log('adding ' + ar[i].xtype);
44565             }
44566             if (ar[i].xtype == 'Button') {  
44567                 //console.log('adding button');
44568                 //console.log(ar[i]);
44569                 this.addButton(ar[i]);
44570                 this.allItems.push(fe);
44571                 continue;
44572             }
44573             
44574             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44575                 alert('end is not supported on xtype any more, use items');
44576             //    this.end();
44577             //    //console.log('adding end');
44578             }
44579             
44580         }
44581         return ret;
44582     },
44583     
44584     /**
44585      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44586      * option "monitorValid"
44587      */
44588     startMonitoring : function(){
44589         if(!this.bound){
44590             this.bound = true;
44591             Roo.TaskMgr.start({
44592                 run : this.bindHandler,
44593                 interval : this.monitorPoll || 200,
44594                 scope: this
44595             });
44596         }
44597     },
44598
44599     /**
44600      * Stops monitoring of the valid state of this form
44601      */
44602     stopMonitoring : function(){
44603         this.bound = false;
44604     },
44605
44606     // private
44607     bindHandler : function(){
44608         if(!this.bound){
44609             return false; // stops binding
44610         }
44611         var valid = true;
44612         this.items.each(function(f){
44613             if(!f.isValid(true)){
44614                 valid = false;
44615                 return false;
44616             }
44617         });
44618         for(var i = 0, len = this.buttons.length; i < len; i++){
44619             var btn = this.buttons[i];
44620             if(btn.formBind === true && btn.disabled === valid){
44621                 btn.setDisabled(!valid);
44622             }
44623         }
44624         this.fireEvent('clientvalidation', this, valid);
44625     }
44626     
44627     
44628     
44629     
44630     
44631     
44632     
44633     
44634 });
44635
44636
44637 // back compat
44638 Roo.Form = Roo.form.Form;
44639 /*
44640  * Based on:
44641  * Ext JS Library 1.1.1
44642  * Copyright(c) 2006-2007, Ext JS, LLC.
44643  *
44644  * Originally Released Under LGPL - original licence link has changed is not relivant.
44645  *
44646  * Fork - LGPL
44647  * <script type="text/javascript">
44648  */
44649  
44650  /**
44651  * @class Roo.form.Action
44652  * Internal Class used to handle form actions
44653  * @constructor
44654  * @param {Roo.form.BasicForm} el The form element or its id
44655  * @param {Object} config Configuration options
44656  */
44657  
44658  
44659 // define the action interface
44660 Roo.form.Action = function(form, options){
44661     this.form = form;
44662     this.options = options || {};
44663 };
44664 /**
44665  * Client Validation Failed
44666  * @const 
44667  */
44668 Roo.form.Action.CLIENT_INVALID = 'client';
44669 /**
44670  * Server Validation Failed
44671  * @const 
44672  */
44673  Roo.form.Action.SERVER_INVALID = 'server';
44674  /**
44675  * Connect to Server Failed
44676  * @const 
44677  */
44678 Roo.form.Action.CONNECT_FAILURE = 'connect';
44679 /**
44680  * Reading Data from Server Failed
44681  * @const 
44682  */
44683 Roo.form.Action.LOAD_FAILURE = 'load';
44684
44685 Roo.form.Action.prototype = {
44686     type : 'default',
44687     failureType : undefined,
44688     response : undefined,
44689     result : undefined,
44690
44691     // interface method
44692     run : function(options){
44693
44694     },
44695
44696     // interface method
44697     success : function(response){
44698
44699     },
44700
44701     // interface method
44702     handleResponse : function(response){
44703
44704     },
44705
44706     // default connection failure
44707     failure : function(response){
44708         
44709         this.response = response;
44710         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44711         this.form.afterAction(this, false);
44712     },
44713
44714     processResponse : function(response){
44715         this.response = response;
44716         if(!response.responseText){
44717             return true;
44718         }
44719         this.result = this.handleResponse(response);
44720         return this.result;
44721     },
44722
44723     // utility functions used internally
44724     getUrl : function(appendParams){
44725         var url = this.options.url || this.form.url || this.form.el.dom.action;
44726         if(appendParams){
44727             var p = this.getParams();
44728             if(p){
44729                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44730             }
44731         }
44732         return url;
44733     },
44734
44735     getMethod : function(){
44736         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44737     },
44738
44739     getParams : function(){
44740         var bp = this.form.baseParams;
44741         var p = this.options.params;
44742         if(p){
44743             if(typeof p == "object"){
44744                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44745             }else if(typeof p == 'string' && bp){
44746                 p += '&' + Roo.urlEncode(bp);
44747             }
44748         }else if(bp){
44749             p = Roo.urlEncode(bp);
44750         }
44751         return p;
44752     },
44753
44754     createCallback : function(){
44755         return {
44756             success: this.success,
44757             failure: this.failure,
44758             scope: this,
44759             timeout: (this.form.timeout*1000),
44760             upload: this.form.fileUpload ? this.success : undefined
44761         };
44762     }
44763 };
44764
44765 Roo.form.Action.Submit = function(form, options){
44766     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44767 };
44768
44769 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44770     type : 'submit',
44771
44772     haveProgress : false,
44773     uploadComplete : false,
44774     
44775     // uploadProgress indicator.
44776     uploadProgress : function()
44777     {
44778         if (!this.form.progressUrl) {
44779             return;
44780         }
44781         
44782         if (!this.haveProgress) {
44783             Roo.MessageBox.progress("Uploading", "Uploading");
44784         }
44785         if (this.uploadComplete) {
44786            Roo.MessageBox.hide();
44787            return;
44788         }
44789         
44790         this.haveProgress = true;
44791    
44792         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44793         
44794         var c = new Roo.data.Connection();
44795         c.request({
44796             url : this.form.progressUrl,
44797             params: {
44798                 id : uid
44799             },
44800             method: 'GET',
44801             success : function(req){
44802                //console.log(data);
44803                 var rdata = false;
44804                 var edata;
44805                 try  {
44806                    rdata = Roo.decode(req.responseText)
44807                 } catch (e) {
44808                     Roo.log("Invalid data from server..");
44809                     Roo.log(edata);
44810                     return;
44811                 }
44812                 if (!rdata || !rdata.success) {
44813                     Roo.log(rdata);
44814                     Roo.MessageBox.alert(Roo.encode(rdata));
44815                     return;
44816                 }
44817                 var data = rdata.data;
44818                 
44819                 if (this.uploadComplete) {
44820                    Roo.MessageBox.hide();
44821                    return;
44822                 }
44823                    
44824                 if (data){
44825                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44826                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44827                     );
44828                 }
44829                 this.uploadProgress.defer(2000,this);
44830             },
44831        
44832             failure: function(data) {
44833                 Roo.log('progress url failed ');
44834                 Roo.log(data);
44835             },
44836             scope : this
44837         });
44838            
44839     },
44840     
44841     
44842     run : function()
44843     {
44844         // run get Values on the form, so it syncs any secondary forms.
44845         this.form.getValues();
44846         
44847         var o = this.options;
44848         var method = this.getMethod();
44849         var isPost = method == 'POST';
44850         if(o.clientValidation === false || this.form.isValid()){
44851             
44852             if (this.form.progressUrl) {
44853                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44854                     (new Date() * 1) + '' + Math.random());
44855                     
44856             } 
44857             
44858             
44859             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44860                 form:this.form.el.dom,
44861                 url:this.getUrl(!isPost),
44862                 method: method,
44863                 params:isPost ? this.getParams() : null,
44864                 isUpload: this.form.fileUpload
44865             }));
44866             
44867             this.uploadProgress();
44868
44869         }else if (o.clientValidation !== false){ // client validation failed
44870             this.failureType = Roo.form.Action.CLIENT_INVALID;
44871             this.form.afterAction(this, false);
44872         }
44873     },
44874
44875     success : function(response)
44876     {
44877         this.uploadComplete= true;
44878         if (this.haveProgress) {
44879             Roo.MessageBox.hide();
44880         }
44881         
44882         
44883         var result = this.processResponse(response);
44884         if(result === true || result.success){
44885             this.form.afterAction(this, true);
44886             return;
44887         }
44888         if(result.errors){
44889             this.form.markInvalid(result.errors);
44890             this.failureType = Roo.form.Action.SERVER_INVALID;
44891         }
44892         this.form.afterAction(this, false);
44893     },
44894     failure : function(response)
44895     {
44896         this.uploadComplete= true;
44897         if (this.haveProgress) {
44898             Roo.MessageBox.hide();
44899         }
44900         
44901         this.response = response;
44902         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44903         this.form.afterAction(this, false);
44904     },
44905     
44906     handleResponse : function(response){
44907         if(this.form.errorReader){
44908             var rs = this.form.errorReader.read(response);
44909             var errors = [];
44910             if(rs.records){
44911                 for(var i = 0, len = rs.records.length; i < len; i++) {
44912                     var r = rs.records[i];
44913                     errors[i] = r.data;
44914                 }
44915             }
44916             if(errors.length < 1){
44917                 errors = null;
44918             }
44919             return {
44920                 success : rs.success,
44921                 errors : errors
44922             };
44923         }
44924         var ret = false;
44925         try {
44926             ret = Roo.decode(response.responseText);
44927         } catch (e) {
44928             ret = {
44929                 success: false,
44930                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44931                 errors : []
44932             };
44933         }
44934         return ret;
44935         
44936     }
44937 });
44938
44939
44940 Roo.form.Action.Load = function(form, options){
44941     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44942     this.reader = this.form.reader;
44943 };
44944
44945 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44946     type : 'load',
44947
44948     run : function(){
44949         
44950         Roo.Ajax.request(Roo.apply(
44951                 this.createCallback(), {
44952                     method:this.getMethod(),
44953                     url:this.getUrl(false),
44954                     params:this.getParams()
44955         }));
44956     },
44957
44958     success : function(response){
44959         
44960         var result = this.processResponse(response);
44961         if(result === true || !result.success || !result.data){
44962             this.failureType = Roo.form.Action.LOAD_FAILURE;
44963             this.form.afterAction(this, false);
44964             return;
44965         }
44966         this.form.clearInvalid();
44967         this.form.setValues(result.data);
44968         this.form.afterAction(this, true);
44969     },
44970
44971     handleResponse : function(response){
44972         if(this.form.reader){
44973             var rs = this.form.reader.read(response);
44974             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44975             return {
44976                 success : rs.success,
44977                 data : data
44978             };
44979         }
44980         return Roo.decode(response.responseText);
44981     }
44982 });
44983
44984 Roo.form.Action.ACTION_TYPES = {
44985     'load' : Roo.form.Action.Load,
44986     'submit' : Roo.form.Action.Submit
44987 };/*
44988  * Based on:
44989  * Ext JS Library 1.1.1
44990  * Copyright(c) 2006-2007, Ext JS, LLC.
44991  *
44992  * Originally Released Under LGPL - original licence link has changed is not relivant.
44993  *
44994  * Fork - LGPL
44995  * <script type="text/javascript">
44996  */
44997  
44998 /**
44999  * @class Roo.form.Layout
45000  * @extends Roo.Component
45001  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45002  * @constructor
45003  * @param {Object} config Configuration options
45004  */
45005 Roo.form.Layout = function(config){
45006     var xitems = [];
45007     if (config.items) {
45008         xitems = config.items;
45009         delete config.items;
45010     }
45011     Roo.form.Layout.superclass.constructor.call(this, config);
45012     this.stack = [];
45013     Roo.each(xitems, this.addxtype, this);
45014      
45015 };
45016
45017 Roo.extend(Roo.form.Layout, Roo.Component, {
45018     /**
45019      * @cfg {String/Object} autoCreate
45020      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45021      */
45022     /**
45023      * @cfg {String/Object/Function} style
45024      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45025      * a function which returns such a specification.
45026      */
45027     /**
45028      * @cfg {String} labelAlign
45029      * Valid values are "left," "top" and "right" (defaults to "left")
45030      */
45031     /**
45032      * @cfg {Number} labelWidth
45033      * Fixed width in pixels of all field labels (defaults to undefined)
45034      */
45035     /**
45036      * @cfg {Boolean} clear
45037      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45038      */
45039     clear : true,
45040     /**
45041      * @cfg {String} labelSeparator
45042      * The separator to use after field labels (defaults to ':')
45043      */
45044     labelSeparator : ':',
45045     /**
45046      * @cfg {Boolean} hideLabels
45047      * True to suppress the display of field labels in this layout (defaults to false)
45048      */
45049     hideLabels : false,
45050
45051     // private
45052     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45053     
45054     isLayout : true,
45055     
45056     // private
45057     onRender : function(ct, position){
45058         if(this.el){ // from markup
45059             this.el = Roo.get(this.el);
45060         }else {  // generate
45061             var cfg = this.getAutoCreate();
45062             this.el = ct.createChild(cfg, position);
45063         }
45064         if(this.style){
45065             this.el.applyStyles(this.style);
45066         }
45067         if(this.labelAlign){
45068             this.el.addClass('x-form-label-'+this.labelAlign);
45069         }
45070         if(this.hideLabels){
45071             this.labelStyle = "display:none";
45072             this.elementStyle = "padding-left:0;";
45073         }else{
45074             if(typeof this.labelWidth == 'number'){
45075                 this.labelStyle = "width:"+this.labelWidth+"px;";
45076                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45077             }
45078             if(this.labelAlign == 'top'){
45079                 this.labelStyle = "width:auto;";
45080                 this.elementStyle = "padding-left:0;";
45081             }
45082         }
45083         var stack = this.stack;
45084         var slen = stack.length;
45085         if(slen > 0){
45086             if(!this.fieldTpl){
45087                 var t = new Roo.Template(
45088                     '<div class="x-form-item {5}">',
45089                         '<label for="{0}" style="{2}">{1}{4}</label>',
45090                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45091                         '</div>',
45092                     '</div><div class="x-form-clear-left"></div>'
45093                 );
45094                 t.disableFormats = true;
45095                 t.compile();
45096                 Roo.form.Layout.prototype.fieldTpl = t;
45097             }
45098             for(var i = 0; i < slen; i++) {
45099                 if(stack[i].isFormField){
45100                     this.renderField(stack[i]);
45101                 }else{
45102                     this.renderComponent(stack[i]);
45103                 }
45104             }
45105         }
45106         if(this.clear){
45107             this.el.createChild({cls:'x-form-clear'});
45108         }
45109     },
45110
45111     // private
45112     renderField : function(f){
45113         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45114                f.id, //0
45115                f.fieldLabel, //1
45116                f.labelStyle||this.labelStyle||'', //2
45117                this.elementStyle||'', //3
45118                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45119                f.itemCls||this.itemCls||''  //5
45120        ], true).getPrevSibling());
45121     },
45122
45123     // private
45124     renderComponent : function(c){
45125         c.render(c.isLayout ? this.el : this.el.createChild());    
45126     },
45127     /**
45128      * Adds a object form elements (using the xtype property as the factory method.)
45129      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45130      * @param {Object} config 
45131      */
45132     addxtype : function(o)
45133     {
45134         // create the lement.
45135         o.form = this.form;
45136         var fe = Roo.factory(o, Roo.form);
45137         this.form.allItems.push(fe);
45138         this.stack.push(fe);
45139         
45140         if (fe.isFormField) {
45141             this.form.items.add(fe);
45142         }
45143          
45144         return fe;
45145     }
45146 });
45147
45148 /**
45149  * @class Roo.form.Column
45150  * @extends Roo.form.Layout
45151  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45152  * @constructor
45153  * @param {Object} config Configuration options
45154  */
45155 Roo.form.Column = function(config){
45156     Roo.form.Column.superclass.constructor.call(this, config);
45157 };
45158
45159 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45160     /**
45161      * @cfg {Number/String} width
45162      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45163      */
45164     /**
45165      * @cfg {String/Object} autoCreate
45166      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45167      */
45168
45169     // private
45170     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45171
45172     // private
45173     onRender : function(ct, position){
45174         Roo.form.Column.superclass.onRender.call(this, ct, position);
45175         if(this.width){
45176             this.el.setWidth(this.width);
45177         }
45178     }
45179 });
45180
45181
45182 /**
45183  * @class Roo.form.Row
45184  * @extends Roo.form.Layout
45185  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45186  * @constructor
45187  * @param {Object} config Configuration options
45188  */
45189
45190  
45191 Roo.form.Row = function(config){
45192     Roo.form.Row.superclass.constructor.call(this, config);
45193 };
45194  
45195 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45196       /**
45197      * @cfg {Number/String} width
45198      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45199      */
45200     /**
45201      * @cfg {Number/String} height
45202      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45203      */
45204     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45205     
45206     padWidth : 20,
45207     // private
45208     onRender : function(ct, position){
45209         //console.log('row render');
45210         if(!this.rowTpl){
45211             var t = new Roo.Template(
45212                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45213                     '<label for="{0}" style="{2}">{1}{4}</label>',
45214                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45215                     '</div>',
45216                 '</div>'
45217             );
45218             t.disableFormats = true;
45219             t.compile();
45220             Roo.form.Layout.prototype.rowTpl = t;
45221         }
45222         this.fieldTpl = this.rowTpl;
45223         
45224         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45225         var labelWidth = 100;
45226         
45227         if ((this.labelAlign != 'top')) {
45228             if (typeof this.labelWidth == 'number') {
45229                 labelWidth = this.labelWidth
45230             }
45231             this.padWidth =  20 + labelWidth;
45232             
45233         }
45234         
45235         Roo.form.Column.superclass.onRender.call(this, ct, position);
45236         if(this.width){
45237             this.el.setWidth(this.width);
45238         }
45239         if(this.height){
45240             this.el.setHeight(this.height);
45241         }
45242     },
45243     
45244     // private
45245     renderField : function(f){
45246         f.fieldEl = this.fieldTpl.append(this.el, [
45247                f.id, f.fieldLabel,
45248                f.labelStyle||this.labelStyle||'',
45249                this.elementStyle||'',
45250                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45251                f.itemCls||this.itemCls||'',
45252                f.width ? f.width + this.padWidth : 160 + this.padWidth
45253        ],true);
45254     }
45255 });
45256  
45257
45258 /**
45259  * @class Roo.form.FieldSet
45260  * @extends Roo.form.Layout
45261  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45262  * @constructor
45263  * @param {Object} config Configuration options
45264  */
45265 Roo.form.FieldSet = function(config){
45266     Roo.form.FieldSet.superclass.constructor.call(this, config);
45267 };
45268
45269 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45270     /**
45271      * @cfg {String} legend
45272      * The text to display as the legend for the FieldSet (defaults to '')
45273      */
45274     /**
45275      * @cfg {String/Object} autoCreate
45276      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45277      */
45278
45279     // private
45280     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45281
45282     // private
45283     onRender : function(ct, position){
45284         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45285         if(this.legend){
45286             this.setLegend(this.legend);
45287         }
45288     },
45289
45290     // private
45291     setLegend : function(text){
45292         if(this.rendered){
45293             this.el.child('legend').update(text);
45294         }
45295     }
45296 });/*
45297  * Based on:
45298  * Ext JS Library 1.1.1
45299  * Copyright(c) 2006-2007, Ext JS, LLC.
45300  *
45301  * Originally Released Under LGPL - original licence link has changed is not relivant.
45302  *
45303  * Fork - LGPL
45304  * <script type="text/javascript">
45305  */
45306 /**
45307  * @class Roo.form.VTypes
45308  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45309  * @singleton
45310  */
45311 Roo.form.VTypes = function(){
45312     // closure these in so they are only created once.
45313     var alpha = /^[a-zA-Z_]+$/;
45314     var alphanum = /^[a-zA-Z0-9_]+$/;
45315     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45316     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45317
45318     // All these messages and functions are configurable
45319     return {
45320         /**
45321          * The function used to validate email addresses
45322          * @param {String} value The email address
45323          */
45324         'email' : function(v){
45325             return email.test(v);
45326         },
45327         /**
45328          * The error text to display when the email validation function returns false
45329          * @type String
45330          */
45331         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45332         /**
45333          * The keystroke filter mask to be applied on email input
45334          * @type RegExp
45335          */
45336         'emailMask' : /[a-z0-9_\.\-@]/i,
45337
45338         /**
45339          * The function used to validate URLs
45340          * @param {String} value The URL
45341          */
45342         'url' : function(v){
45343             return url.test(v);
45344         },
45345         /**
45346          * The error text to display when the url validation function returns false
45347          * @type String
45348          */
45349         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45350         
45351         /**
45352          * The function used to validate alpha values
45353          * @param {String} value The value
45354          */
45355         'alpha' : function(v){
45356             return alpha.test(v);
45357         },
45358         /**
45359          * The error text to display when the alpha validation function returns false
45360          * @type String
45361          */
45362         'alphaText' : 'This field should only contain letters and _',
45363         /**
45364          * The keystroke filter mask to be applied on alpha input
45365          * @type RegExp
45366          */
45367         'alphaMask' : /[a-z_]/i,
45368
45369         /**
45370          * The function used to validate alphanumeric values
45371          * @param {String} value The value
45372          */
45373         'alphanum' : function(v){
45374             return alphanum.test(v);
45375         },
45376         /**
45377          * The error text to display when the alphanumeric validation function returns false
45378          * @type String
45379          */
45380         'alphanumText' : 'This field should only contain letters, numbers and _',
45381         /**
45382          * The keystroke filter mask to be applied on alphanumeric input
45383          * @type RegExp
45384          */
45385         'alphanumMask' : /[a-z0-9_]/i
45386     };
45387 }();//<script type="text/javascript">
45388
45389 /**
45390  * @class Roo.form.FCKeditor
45391  * @extends Roo.form.TextArea
45392  * Wrapper around the FCKEditor http://www.fckeditor.net
45393  * @constructor
45394  * Creates a new FCKeditor
45395  * @param {Object} config Configuration options
45396  */
45397 Roo.form.FCKeditor = function(config){
45398     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45399     this.addEvents({
45400          /**
45401          * @event editorinit
45402          * Fired when the editor is initialized - you can add extra handlers here..
45403          * @param {FCKeditor} this
45404          * @param {Object} the FCK object.
45405          */
45406         editorinit : true
45407     });
45408     
45409     
45410 };
45411 Roo.form.FCKeditor.editors = { };
45412 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45413 {
45414     //defaultAutoCreate : {
45415     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45416     //},
45417     // private
45418     /**
45419      * @cfg {Object} fck options - see fck manual for details.
45420      */
45421     fckconfig : false,
45422     
45423     /**
45424      * @cfg {Object} fck toolbar set (Basic or Default)
45425      */
45426     toolbarSet : 'Basic',
45427     /**
45428      * @cfg {Object} fck BasePath
45429      */ 
45430     basePath : '/fckeditor/',
45431     
45432     
45433     frame : false,
45434     
45435     value : '',
45436     
45437    
45438     onRender : function(ct, position)
45439     {
45440         if(!this.el){
45441             this.defaultAutoCreate = {
45442                 tag: "textarea",
45443                 style:"width:300px;height:60px;",
45444                 autocomplete: "off"
45445             };
45446         }
45447         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45448         /*
45449         if(this.grow){
45450             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45451             if(this.preventScrollbars){
45452                 this.el.setStyle("overflow", "hidden");
45453             }
45454             this.el.setHeight(this.growMin);
45455         }
45456         */
45457         //console.log('onrender' + this.getId() );
45458         Roo.form.FCKeditor.editors[this.getId()] = this;
45459          
45460
45461         this.replaceTextarea() ;
45462         
45463     },
45464     
45465     getEditor : function() {
45466         return this.fckEditor;
45467     },
45468     /**
45469      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45470      * @param {Mixed} value The value to set
45471      */
45472     
45473     
45474     setValue : function(value)
45475     {
45476         //console.log('setValue: ' + value);
45477         
45478         if(typeof(value) == 'undefined') { // not sure why this is happending...
45479             return;
45480         }
45481         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45482         
45483         //if(!this.el || !this.getEditor()) {
45484         //    this.value = value;
45485             //this.setValue.defer(100,this,[value]);    
45486         //    return;
45487         //} 
45488         
45489         if(!this.getEditor()) {
45490             return;
45491         }
45492         
45493         this.getEditor().SetData(value);
45494         
45495         //
45496
45497     },
45498
45499     /**
45500      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45501      * @return {Mixed} value The field value
45502      */
45503     getValue : function()
45504     {
45505         
45506         if (this.frame && this.frame.dom.style.display == 'none') {
45507             return Roo.form.FCKeditor.superclass.getValue.call(this);
45508         }
45509         
45510         if(!this.el || !this.getEditor()) {
45511            
45512            // this.getValue.defer(100,this); 
45513             return this.value;
45514         }
45515        
45516         
45517         var value=this.getEditor().GetData();
45518         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45519         return Roo.form.FCKeditor.superclass.getValue.call(this);
45520         
45521
45522     },
45523
45524     /**
45525      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45526      * @return {Mixed} value The field value
45527      */
45528     getRawValue : function()
45529     {
45530         if (this.frame && this.frame.dom.style.display == 'none') {
45531             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45532         }
45533         
45534         if(!this.el || !this.getEditor()) {
45535             //this.getRawValue.defer(100,this); 
45536             return this.value;
45537             return;
45538         }
45539         
45540         
45541         
45542         var value=this.getEditor().GetData();
45543         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45544         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45545          
45546     },
45547     
45548     setSize : function(w,h) {
45549         
45550         
45551         
45552         //if (this.frame && this.frame.dom.style.display == 'none') {
45553         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45554         //    return;
45555         //}
45556         //if(!this.el || !this.getEditor()) {
45557         //    this.setSize.defer(100,this, [w,h]); 
45558         //    return;
45559         //}
45560         
45561         
45562         
45563         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45564         
45565         this.frame.dom.setAttribute('width', w);
45566         this.frame.dom.setAttribute('height', h);
45567         this.frame.setSize(w,h);
45568         
45569     },
45570     
45571     toggleSourceEdit : function(value) {
45572         
45573       
45574          
45575         this.el.dom.style.display = value ? '' : 'none';
45576         this.frame.dom.style.display = value ?  'none' : '';
45577         
45578     },
45579     
45580     
45581     focus: function(tag)
45582     {
45583         if (this.frame.dom.style.display == 'none') {
45584             return Roo.form.FCKeditor.superclass.focus.call(this);
45585         }
45586         if(!this.el || !this.getEditor()) {
45587             this.focus.defer(100,this, [tag]); 
45588             return;
45589         }
45590         
45591         
45592         
45593         
45594         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45595         this.getEditor().Focus();
45596         if (tgs.length) {
45597             if (!this.getEditor().Selection.GetSelection()) {
45598                 this.focus.defer(100,this, [tag]); 
45599                 return;
45600             }
45601             
45602             
45603             var r = this.getEditor().EditorDocument.createRange();
45604             r.setStart(tgs[0],0);
45605             r.setEnd(tgs[0],0);
45606             this.getEditor().Selection.GetSelection().removeAllRanges();
45607             this.getEditor().Selection.GetSelection().addRange(r);
45608             this.getEditor().Focus();
45609         }
45610         
45611     },
45612     
45613     
45614     
45615     replaceTextarea : function()
45616     {
45617         if ( document.getElementById( this.getId() + '___Frame' ) )
45618             return ;
45619         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45620         //{
45621             // We must check the elements firstly using the Id and then the name.
45622         var oTextarea = document.getElementById( this.getId() );
45623         
45624         var colElementsByName = document.getElementsByName( this.getId() ) ;
45625          
45626         oTextarea.style.display = 'none' ;
45627
45628         if ( oTextarea.tabIndex ) {            
45629             this.TabIndex = oTextarea.tabIndex ;
45630         }
45631         
45632         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45633         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45634         this.frame = Roo.get(this.getId() + '___Frame')
45635     },
45636     
45637     _getConfigHtml : function()
45638     {
45639         var sConfig = '' ;
45640
45641         for ( var o in this.fckconfig ) {
45642             sConfig += sConfig.length > 0  ? '&amp;' : '';
45643             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45644         }
45645
45646         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45647     },
45648     
45649     
45650     _getIFrameHtml : function()
45651     {
45652         var sFile = 'fckeditor.html' ;
45653         /* no idea what this is about..
45654         try
45655         {
45656             if ( (/fcksource=true/i).test( window.top.location.search ) )
45657                 sFile = 'fckeditor.original.html' ;
45658         }
45659         catch (e) { 
45660         */
45661
45662         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45663         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45664         
45665         
45666         var html = '<iframe id="' + this.getId() +
45667             '___Frame" src="' + sLink +
45668             '" width="' + this.width +
45669             '" height="' + this.height + '"' +
45670             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45671             ' frameborder="0" scrolling="no"></iframe>' ;
45672
45673         return html ;
45674     },
45675     
45676     _insertHtmlBefore : function( html, element )
45677     {
45678         if ( element.insertAdjacentHTML )       {
45679             // IE
45680             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45681         } else { // Gecko
45682             var oRange = document.createRange() ;
45683             oRange.setStartBefore( element ) ;
45684             var oFragment = oRange.createContextualFragment( html );
45685             element.parentNode.insertBefore( oFragment, element ) ;
45686         }
45687     }
45688     
45689     
45690   
45691     
45692     
45693     
45694     
45695
45696 });
45697
45698 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45699
45700 function FCKeditor_OnComplete(editorInstance){
45701     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45702     f.fckEditor = editorInstance;
45703     //console.log("loaded");
45704     f.fireEvent('editorinit', f, editorInstance);
45705
45706   
45707
45708  
45709
45710
45711
45712
45713
45714
45715
45716
45717
45718
45719
45720
45721
45722
45723
45724 //<script type="text/javascript">
45725 /**
45726  * @class Roo.form.GridField
45727  * @extends Roo.form.Field
45728  * Embed a grid (or editable grid into a form)
45729  * STATUS ALPHA
45730  * 
45731  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45732  * it needs 
45733  * xgrid.store = Roo.data.Store
45734  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45735  * xgrid.store.reader = Roo.data.JsonReader 
45736  * 
45737  * 
45738  * @constructor
45739  * Creates a new GridField
45740  * @param {Object} config Configuration options
45741  */
45742 Roo.form.GridField = function(config){
45743     Roo.form.GridField.superclass.constructor.call(this, config);
45744      
45745 };
45746
45747 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45748     /**
45749      * @cfg {Number} width  - used to restrict width of grid..
45750      */
45751     width : 100,
45752     /**
45753      * @cfg {Number} height - used to restrict height of grid..
45754      */
45755     height : 50,
45756      /**
45757      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45758          * 
45759          *}
45760      */
45761     xgrid : false, 
45762     /**
45763      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45764      * {tag: "input", type: "checkbox", autocomplete: "off"})
45765      */
45766    // defaultAutoCreate : { tag: 'div' },
45767     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45768     /**
45769      * @cfg {String} addTitle Text to include for adding a title.
45770      */
45771     addTitle : false,
45772     //
45773     onResize : function(){
45774         Roo.form.Field.superclass.onResize.apply(this, arguments);
45775     },
45776
45777     initEvents : function(){
45778         // Roo.form.Checkbox.superclass.initEvents.call(this);
45779         // has no events...
45780        
45781     },
45782
45783
45784     getResizeEl : function(){
45785         return this.wrap;
45786     },
45787
45788     getPositionEl : function(){
45789         return this.wrap;
45790     },
45791
45792     // private
45793     onRender : function(ct, position){
45794         
45795         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45796         var style = this.style;
45797         delete this.style;
45798         
45799         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45800         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45801         this.viewEl = this.wrap.createChild({ tag: 'div' });
45802         if (style) {
45803             this.viewEl.applyStyles(style);
45804         }
45805         if (this.width) {
45806             this.viewEl.setWidth(this.width);
45807         }
45808         if (this.height) {
45809             this.viewEl.setHeight(this.height);
45810         }
45811         //if(this.inputValue !== undefined){
45812         //this.setValue(this.value);
45813         
45814         
45815         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45816         
45817         
45818         this.grid.render();
45819         this.grid.getDataSource().on('remove', this.refreshValue, this);
45820         this.grid.getDataSource().on('update', this.refreshValue, this);
45821         this.grid.on('afteredit', this.refreshValue, this);
45822  
45823     },
45824      
45825     
45826     /**
45827      * Sets the value of the item. 
45828      * @param {String} either an object  or a string..
45829      */
45830     setValue : function(v){
45831         //this.value = v;
45832         v = v || []; // empty set..
45833         // this does not seem smart - it really only affects memoryproxy grids..
45834         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45835             var ds = this.grid.getDataSource();
45836             // assumes a json reader..
45837             var data = {}
45838             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45839             ds.loadData( data);
45840         }
45841         // clear selection so it does not get stale.
45842         if (this.grid.sm) { 
45843             this.grid.sm.clearSelections();
45844         }
45845         
45846         Roo.form.GridField.superclass.setValue.call(this, v);
45847         this.refreshValue();
45848         // should load data in the grid really....
45849     },
45850     
45851     // private
45852     refreshValue: function() {
45853          var val = [];
45854         this.grid.getDataSource().each(function(r) {
45855             val.push(r.data);
45856         });
45857         this.el.dom.value = Roo.encode(val);
45858     }
45859     
45860      
45861     
45862     
45863 });/*
45864  * Based on:
45865  * Ext JS Library 1.1.1
45866  * Copyright(c) 2006-2007, Ext JS, LLC.
45867  *
45868  * Originally Released Under LGPL - original licence link has changed is not relivant.
45869  *
45870  * Fork - LGPL
45871  * <script type="text/javascript">
45872  */
45873 /**
45874  * @class Roo.form.DisplayField
45875  * @extends Roo.form.Field
45876  * A generic Field to display non-editable data.
45877  * @constructor
45878  * Creates a new Display Field item.
45879  * @param {Object} config Configuration options
45880  */
45881 Roo.form.DisplayField = function(config){
45882     Roo.form.DisplayField.superclass.constructor.call(this, config);
45883     
45884 };
45885
45886 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45887     inputType:      'hidden',
45888     allowBlank:     true,
45889     readOnly:         true,
45890     
45891  
45892     /**
45893      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45894      */
45895     focusClass : undefined,
45896     /**
45897      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45898      */
45899     fieldClass: 'x-form-field',
45900     
45901      /**
45902      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45903      */
45904     valueRenderer: undefined,
45905     
45906     width: 100,
45907     /**
45908      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45909      * {tag: "input", type: "checkbox", autocomplete: "off"})
45910      */
45911      
45912  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45913
45914     onResize : function(){
45915         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45916         
45917     },
45918
45919     initEvents : function(){
45920         // Roo.form.Checkbox.superclass.initEvents.call(this);
45921         // has no events...
45922        
45923     },
45924
45925
45926     getResizeEl : function(){
45927         return this.wrap;
45928     },
45929
45930     getPositionEl : function(){
45931         return this.wrap;
45932     },
45933
45934     // private
45935     onRender : function(ct, position){
45936         
45937         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45938         //if(this.inputValue !== undefined){
45939         this.wrap = this.el.wrap();
45940         
45941         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45942         
45943         if (this.bodyStyle) {
45944             this.viewEl.applyStyles(this.bodyStyle);
45945         }
45946         //this.viewEl.setStyle('padding', '2px');
45947         
45948         this.setValue(this.value);
45949         
45950     },
45951 /*
45952     // private
45953     initValue : Roo.emptyFn,
45954
45955   */
45956
45957         // private
45958     onClick : function(){
45959         
45960     },
45961
45962     /**
45963      * Sets the checked state of the checkbox.
45964      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45965      */
45966     setValue : function(v){
45967         this.value = v;
45968         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45969         // this might be called before we have a dom element..
45970         if (!this.viewEl) {
45971             return;
45972         }
45973         this.viewEl.dom.innerHTML = html;
45974         Roo.form.DisplayField.superclass.setValue.call(this, v);
45975
45976     }
45977 });/*
45978  * 
45979  * Licence- LGPL
45980  * 
45981  */
45982
45983 /**
45984  * @class Roo.form.DayPicker
45985  * @extends Roo.form.Field
45986  * A Day picker show [M] [T] [W] ....
45987  * @constructor
45988  * Creates a new Day Picker
45989  * @param {Object} config Configuration options
45990  */
45991 Roo.form.DayPicker= function(config){
45992     Roo.form.DayPicker.superclass.constructor.call(this, config);
45993      
45994 };
45995
45996 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45997     /**
45998      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45999      */
46000     focusClass : undefined,
46001     /**
46002      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46003      */
46004     fieldClass: "x-form-field",
46005    
46006     /**
46007      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46008      * {tag: "input", type: "checkbox", autocomplete: "off"})
46009      */
46010     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46011     
46012    
46013     actionMode : 'viewEl', 
46014     //
46015     // private
46016  
46017     inputType : 'hidden',
46018     
46019      
46020     inputElement: false, // real input element?
46021     basedOn: false, // ????
46022     
46023     isFormField: true, // not sure where this is needed!!!!
46024
46025     onResize : function(){
46026         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46027         if(!this.boxLabel){
46028             this.el.alignTo(this.wrap, 'c-c');
46029         }
46030     },
46031
46032     initEvents : function(){
46033         Roo.form.Checkbox.superclass.initEvents.call(this);
46034         this.el.on("click", this.onClick,  this);
46035         this.el.on("change", this.onClick,  this);
46036     },
46037
46038
46039     getResizeEl : function(){
46040         return this.wrap;
46041     },
46042
46043     getPositionEl : function(){
46044         return this.wrap;
46045     },
46046
46047     
46048     // private
46049     onRender : function(ct, position){
46050         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46051        
46052         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46053         
46054         var r1 = '<table><tr>';
46055         var r2 = '<tr class="x-form-daypick-icons">';
46056         for (var i=0; i < 7; i++) {
46057             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46058             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46059         }
46060         
46061         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46062         viewEl.select('img').on('click', this.onClick, this);
46063         this.viewEl = viewEl;   
46064         
46065         
46066         // this will not work on Chrome!!!
46067         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46068         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46069         
46070         
46071           
46072
46073     },
46074
46075     // private
46076     initValue : Roo.emptyFn,
46077
46078     /**
46079      * Returns the checked state of the checkbox.
46080      * @return {Boolean} True if checked, else false
46081      */
46082     getValue : function(){
46083         return this.el.dom.value;
46084         
46085     },
46086
46087         // private
46088     onClick : function(e){ 
46089         //this.setChecked(!this.checked);
46090         Roo.get(e.target).toggleClass('x-menu-item-checked');
46091         this.refreshValue();
46092         //if(this.el.dom.checked != this.checked){
46093         //    this.setValue(this.el.dom.checked);
46094        // }
46095     },
46096     
46097     // private
46098     refreshValue : function()
46099     {
46100         var val = '';
46101         this.viewEl.select('img',true).each(function(e,i,n)  {
46102             val += e.is(".x-menu-item-checked") ? String(n) : '';
46103         });
46104         this.setValue(val, true);
46105     },
46106
46107     /**
46108      * Sets the checked state of the checkbox.
46109      * On is always based on a string comparison between inputValue and the param.
46110      * @param {Boolean/String} value - the value to set 
46111      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46112      */
46113     setValue : function(v,suppressEvent){
46114         if (!this.el.dom) {
46115             return;
46116         }
46117         var old = this.el.dom.value ;
46118         this.el.dom.value = v;
46119         if (suppressEvent) {
46120             return ;
46121         }
46122          
46123         // update display..
46124         this.viewEl.select('img',true).each(function(e,i,n)  {
46125             
46126             var on = e.is(".x-menu-item-checked");
46127             var newv = v.indexOf(String(n)) > -1;
46128             if (on != newv) {
46129                 e.toggleClass('x-menu-item-checked');
46130             }
46131             
46132         });
46133         
46134         
46135         this.fireEvent('change', this, v, old);
46136         
46137         
46138     },
46139    
46140     // handle setting of hidden value by some other method!!?!?
46141     setFromHidden: function()
46142     {
46143         if(!this.el){
46144             return;
46145         }
46146         //console.log("SET FROM HIDDEN");
46147         //alert('setFrom hidden');
46148         this.setValue(this.el.dom.value);
46149     },
46150     
46151     onDestroy : function()
46152     {
46153         if(this.viewEl){
46154             Roo.get(this.viewEl).remove();
46155         }
46156          
46157         Roo.form.DayPicker.superclass.onDestroy.call(this);
46158     }
46159
46160 });/*
46161  * RooJS Library 1.1.1
46162  * Copyright(c) 2008-2011  Alan Knowles
46163  *
46164  * License - LGPL
46165  */
46166  
46167
46168 /**
46169  * @class Roo.form.ComboCheck
46170  * @extends Roo.form.ComboBox
46171  * A combobox for multiple select items.
46172  *
46173  * FIXME - could do with a reset button..
46174  * 
46175  * @constructor
46176  * Create a new ComboCheck
46177  * @param {Object} config Configuration options
46178  */
46179 Roo.form.ComboCheck = function(config){
46180     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46181     // should verify some data...
46182     // like
46183     // hiddenName = required..
46184     // displayField = required
46185     // valudField == required
46186     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46187     var _t = this;
46188     Roo.each(req, function(e) {
46189         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46190             throw "Roo.form.ComboCheck : missing value for: " + e;
46191         }
46192     });
46193     
46194     
46195 };
46196
46197 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46198      
46199      
46200     editable : false,
46201      
46202     selectedClass: 'x-menu-item-checked', 
46203     
46204     // private
46205     onRender : function(ct, position){
46206         var _t = this;
46207         
46208         
46209         
46210         if(!this.tpl){
46211             var cls = 'x-combo-list';
46212
46213             
46214             this.tpl =  new Roo.Template({
46215                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46216                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46217                    '<span>{' + this.displayField + '}</span>' +
46218                     '</div>' 
46219                 
46220             });
46221         }
46222  
46223         
46224         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46225         this.view.singleSelect = false;
46226         this.view.multiSelect = true;
46227         this.view.toggleSelect = true;
46228         this.pageTb.add(new Roo.Toolbar.Fill(), {
46229             
46230             text: 'Done',
46231             handler: function()
46232             {
46233                 _t.collapse();
46234             }
46235         });
46236     },
46237     
46238     onViewOver : function(e, t){
46239         // do nothing...
46240         return;
46241         
46242     },
46243     
46244     onViewClick : function(doFocus,index){
46245         return;
46246         
46247     },
46248     select: function () {
46249         //Roo.log("SELECT CALLED");
46250     },
46251      
46252     selectByValue : function(xv, scrollIntoView){
46253         var ar = this.getValueArray();
46254         var sels = [];
46255         
46256         Roo.each(ar, function(v) {
46257             if(v === undefined || v === null){
46258                 return;
46259             }
46260             var r = this.findRecord(this.valueField, v);
46261             if(r){
46262                 sels.push(this.store.indexOf(r))
46263                 
46264             }
46265         },this);
46266         this.view.select(sels);
46267         return false;
46268     },
46269     
46270     
46271     
46272     onSelect : function(record, index){
46273        // Roo.log("onselect Called");
46274        // this is only called by the clear button now..
46275         this.view.clearSelections();
46276         this.setValue('[]');
46277         if (this.value != this.valueBefore) {
46278             this.fireEvent('change', this, this.value, this.valueBefore);
46279             this.valueBefore = this.value;
46280         }
46281     },
46282     getValueArray : function()
46283     {
46284         var ar = [] ;
46285         
46286         try {
46287             //Roo.log(this.value);
46288             if (typeof(this.value) == 'undefined') {
46289                 return [];
46290             }
46291             var ar = Roo.decode(this.value);
46292             return  ar instanceof Array ? ar : []; //?? valid?
46293             
46294         } catch(e) {
46295             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46296             return [];
46297         }
46298          
46299     },
46300     expand : function ()
46301     {
46302         
46303         Roo.form.ComboCheck.superclass.expand.call(this);
46304         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46305         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46306         
46307
46308     },
46309     
46310     collapse : function(){
46311         Roo.form.ComboCheck.superclass.collapse.call(this);
46312         var sl = this.view.getSelectedIndexes();
46313         var st = this.store;
46314         var nv = [];
46315         var tv = [];
46316         var r;
46317         Roo.each(sl, function(i) {
46318             r = st.getAt(i);
46319             nv.push(r.get(this.valueField));
46320         },this);
46321         this.setValue(Roo.encode(nv));
46322         if (this.value != this.valueBefore) {
46323
46324             this.fireEvent('change', this, this.value, this.valueBefore);
46325             this.valueBefore = this.value;
46326         }
46327         
46328     },
46329     
46330     setValue : function(v){
46331         // Roo.log(v);
46332         this.value = v;
46333         
46334         var vals = this.getValueArray();
46335         var tv = [];
46336         Roo.each(vals, function(k) {
46337             var r = this.findRecord(this.valueField, k);
46338             if(r){
46339                 tv.push(r.data[this.displayField]);
46340             }else if(this.valueNotFoundText !== undefined){
46341                 tv.push( this.valueNotFoundText );
46342             }
46343         },this);
46344        // Roo.log(tv);
46345         
46346         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46347         this.hiddenField.value = v;
46348         this.value = v;
46349     }
46350     
46351 });/*
46352  * Based on:
46353  * Ext JS Library 1.1.1
46354  * Copyright(c) 2006-2007, Ext JS, LLC.
46355  *
46356  * Originally Released Under LGPL - original licence link has changed is not relivant.
46357  *
46358  * Fork - LGPL
46359  * <script type="text/javascript">
46360  */
46361  
46362 /**
46363  * @class Roo.form.Signature
46364  * @extends Roo.form.Field
46365  * Signature field.  
46366  * @constructor
46367  * 
46368  * @param {Object} config Configuration options
46369  */
46370
46371 Roo.form.Signature = function(config){
46372     Roo.form.Signature.superclass.constructor.call(this, config);
46373     
46374     this.addEvents({// not in used??
46375          /**
46376          * @event confirm
46377          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46378              * @param {Roo.form.Signature} combo This combo box
46379              */
46380         'confirm' : true,
46381         /**
46382          * @event reset
46383          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46384              * @param {Roo.form.ComboBox} combo This combo box
46385              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46386              */
46387         'reset' : true
46388     });
46389 };
46390
46391 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46392     /**
46393      * @cfg {Object} labels Label to use when rendering a form.
46394      * defaults to 
46395      * labels : { 
46396      *      clear : "Clear",
46397      *      confirm : "Confirm"
46398      *  }
46399      */
46400     labels : { 
46401         clear : "Clear",
46402         confirm : "Confirm"
46403     },
46404     /**
46405      * @cfg {Number} width The signature panel width (defaults to 300)
46406      */
46407     width: 300,
46408     /**
46409      * @cfg {Number} height The signature panel height (defaults to 100)
46410      */
46411     height : 100,
46412     /**
46413      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46414      */
46415     allowBlank : false,
46416     
46417     //private
46418     // {Object} signPanel The signature SVG panel element (defaults to {})
46419     signPanel : {},
46420     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46421     isMouseDown : false,
46422     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46423     isConfirmed : false,
46424     // {String} signatureTmp SVG mapping string (defaults to empty string)
46425     signatureTmp : '',
46426     
46427     
46428     defaultAutoCreate : { // modified by initCompnoent..
46429         tag: "input",
46430         type:"hidden"
46431     },
46432
46433     // private
46434     onRender : function(ct, position){
46435         
46436         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46437         
46438         this.wrap = this.el.wrap({
46439             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46440         });
46441         
46442         this.createToolbar(this);
46443         this.signPanel = this.wrap.createChild({
46444                 tag: 'div',
46445                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46446             }, this.el
46447         );
46448             
46449         this.svgID = Roo.id();
46450         this.svgEl = this.signPanel.createChild({
46451               xmlns : 'http://www.w3.org/2000/svg',
46452               tag : 'svg',
46453               id : this.svgID + "-svg",
46454               width: this.width,
46455               height: this.height,
46456               viewBox: '0 0 '+this.width+' '+this.height,
46457               cn : [
46458                 {
46459                     tag: "rect",
46460                     id: this.svgID + "-svg-r",
46461                     width: this.width,
46462                     height: this.height,
46463                     fill: "#ffa"
46464                 },
46465                 {
46466                     tag: "line",
46467                     id: this.svgID + "-svg-l",
46468                     x1: "0", // start
46469                     y1: (this.height*0.8), // start set the line in 80% of height
46470                     x2: this.width, // end
46471                     y2: (this.height*0.8), // end set the line in 80% of height
46472                     'stroke': "#666",
46473                     'stroke-width': "1",
46474                     'stroke-dasharray': "3",
46475                     'shape-rendering': "crispEdges",
46476                     'pointer-events': "none"
46477                 },
46478                 {
46479                     tag: "path",
46480                     id: this.svgID + "-svg-p",
46481                     'stroke': "navy",
46482                     'stroke-width': "3",
46483                     'fill': "none",
46484                     'pointer-events': 'none'
46485                 }
46486               ]
46487         });
46488         this.createSVG();
46489         this.svgBox = this.svgEl.dom.getScreenCTM();
46490     },
46491     createSVG : function(){ 
46492         var svg = this.signPanel;
46493         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46494         var t = this;
46495
46496         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46497         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46498         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46499         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46500         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46501         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46502         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46503         
46504     },
46505     isTouchEvent : function(e){
46506         return e.type.match(/^touch/);
46507     },
46508     getCoords : function (e) {
46509         var pt    = this.svgEl.dom.createSVGPoint();
46510         pt.x = e.clientX; 
46511         pt.y = e.clientY;
46512         if (this.isTouchEvent(e)) {
46513             pt.x =  e.targetTouches[0].clientX 
46514             pt.y = e.targetTouches[0].clientY;
46515         }
46516         var a = this.svgEl.dom.getScreenCTM();
46517         var b = a.inverse();
46518         var mx = pt.matrixTransform(b);
46519         return mx.x + ',' + mx.y;
46520     },
46521     //mouse event headler 
46522     down : function (e) {
46523         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46524         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46525         
46526         this.isMouseDown = true;
46527         
46528         e.preventDefault();
46529     },
46530     move : function (e) {
46531         if (this.isMouseDown) {
46532             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46533             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46534         }
46535         
46536         e.preventDefault();
46537     },
46538     up : function (e) {
46539         this.isMouseDown = false;
46540         var sp = this.signatureTmp.split(' ');
46541         
46542         if(sp.length > 1){
46543             if(!sp[sp.length-2].match(/^L/)){
46544                 sp.pop();
46545                 sp.pop();
46546                 sp.push("");
46547                 this.signatureTmp = sp.join(" ");
46548             }
46549         }
46550         if(this.getValue() != this.signatureTmp){
46551             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46552             this.isConfirmed = false;
46553         }
46554         e.preventDefault();
46555     },
46556     
46557     /**
46558      * Protected method that will not generally be called directly. It
46559      * is called when the editor creates its toolbar. Override this method if you need to
46560      * add custom toolbar buttons.
46561      * @param {HtmlEditor} editor
46562      */
46563     createToolbar : function(editor){
46564          function btn(id, toggle, handler){
46565             var xid = fid + '-'+ id ;
46566             return {
46567                 id : xid,
46568                 cmd : id,
46569                 cls : 'x-btn-icon x-edit-'+id,
46570                 enableToggle:toggle !== false,
46571                 scope: editor, // was editor...
46572                 handler:handler||editor.relayBtnCmd,
46573                 clickEvent:'mousedown',
46574                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46575                 tabIndex:-1
46576             };
46577         }
46578         
46579         
46580         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46581         this.tb = tb;
46582         this.tb.add(
46583            {
46584                 cls : ' x-signature-btn x-signature-'+id,
46585                 scope: editor, // was editor...
46586                 handler: this.reset,
46587                 clickEvent:'mousedown',
46588                 text: this.labels.clear
46589             },
46590             {
46591                  xtype : 'Fill',
46592                  xns: Roo.Toolbar
46593             }, 
46594             {
46595                 cls : '  x-signature-btn x-signature-'+id,
46596                 scope: editor, // was editor...
46597                 handler: this.confirmHandler,
46598                 clickEvent:'mousedown',
46599                 text: this.labels.confirm
46600             }
46601         );
46602     
46603     },
46604     //public
46605     /**
46606      * when user is clicked confirm then show this image.....
46607      * 
46608      * @return {String} Image Data URI
46609      */
46610     getImageDataURI : function(){
46611         var svg = this.svgEl.dom.parentNode.innerHTML;
46612         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46613         return src; 
46614     },
46615     /**
46616      * 
46617      * @return {Boolean} this.isConfirmed
46618      */
46619     getConfirmed : function(){
46620         return this.isConfirmed;
46621     },
46622     /**
46623      * 
46624      * @return {Number} this.width
46625      */
46626     getWidth : function(){
46627         return this.width;
46628     },
46629     /**
46630      * 
46631      * @return {Number} this.height
46632      */
46633     getHeight : function(){
46634         return this.height;
46635     },
46636     // private
46637     getSignature : function(){
46638         return this.signatureTmp;
46639     },
46640     // private
46641     reset : function(){
46642         this.signatureTmp = '';
46643         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46644         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46645         this.isConfirmed = false;
46646         Roo.form.Signature.superclass.reset.call(this);
46647     },
46648     setSignature : function(s){
46649         this.signatureTmp = s;
46650         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46651         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46652         this.setValue(s);
46653         this.isConfirmed = false;
46654         Roo.form.Signature.superclass.reset.call(this);
46655     }, 
46656     test : function(){
46657 //        Roo.log(this.signPanel.dom.contentWindow.up())
46658     },
46659     //private
46660     setConfirmed : function(){
46661         
46662         
46663         
46664 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46665     },
46666     // private
46667     confirmHandler : function(){
46668         if(!this.getSignature()){
46669             return;
46670         }
46671         
46672         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46673         this.setValue(this.getSignature());
46674         this.isConfirmed = true;
46675         
46676         this.fireEvent('confirm', this);
46677     },
46678     // private
46679     // Subclasses should provide the validation implementation by overriding this
46680     validateValue : function(value){
46681         if(this.allowBlank){
46682             return true;
46683         }
46684         
46685         if(this.isConfirmed){
46686             return true;
46687         }
46688         return false;
46689     }
46690 });/*
46691  * Based on:
46692  * Ext JS Library 1.1.1
46693  * Copyright(c) 2006-2007, Ext JS, LLC.
46694  *
46695  * Originally Released Under LGPL - original licence link has changed is not relivant.
46696  *
46697  * Fork - LGPL
46698  * <script type="text/javascript">
46699  */
46700  
46701
46702 /**
46703  * @class Roo.form.ComboBox
46704  * @extends Roo.form.TriggerField
46705  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46706  * @constructor
46707  * Create a new ComboBox.
46708  * @param {Object} config Configuration options
46709  */
46710 Roo.form.Select = function(config){
46711     Roo.form.Select.superclass.constructor.call(this, config);
46712      
46713 };
46714
46715 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46716     /**
46717      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46718      */
46719     /**
46720      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46721      * rendering into an Roo.Editor, defaults to false)
46722      */
46723     /**
46724      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46725      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46726      */
46727     /**
46728      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46729      */
46730     /**
46731      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46732      * the dropdown list (defaults to undefined, with no header element)
46733      */
46734
46735      /**
46736      * @cfg {String/Roo.Template} tpl The template to use to render the output
46737      */
46738      
46739     // private
46740     defaultAutoCreate : {tag: "select"  },
46741     /**
46742      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46743      */
46744     listWidth: undefined,
46745     /**
46746      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46747      * mode = 'remote' or 'text' if mode = 'local')
46748      */
46749     displayField: undefined,
46750     /**
46751      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46752      * mode = 'remote' or 'value' if mode = 'local'). 
46753      * Note: use of a valueField requires the user make a selection
46754      * in order for a value to be mapped.
46755      */
46756     valueField: undefined,
46757     
46758     
46759     /**
46760      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46761      * field's data value (defaults to the underlying DOM element's name)
46762      */
46763     hiddenName: undefined,
46764     /**
46765      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46766      */
46767     listClass: '',
46768     /**
46769      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46770      */
46771     selectedClass: 'x-combo-selected',
46772     /**
46773      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46774      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46775      * which displays a downward arrow icon).
46776      */
46777     triggerClass : 'x-form-arrow-trigger',
46778     /**
46779      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46780      */
46781     shadow:'sides',
46782     /**
46783      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46784      * anchor positions (defaults to 'tl-bl')
46785      */
46786     listAlign: 'tl-bl?',
46787     /**
46788      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46789      */
46790     maxHeight: 300,
46791     /**
46792      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46793      * query specified by the allQuery config option (defaults to 'query')
46794      */
46795     triggerAction: 'query',
46796     /**
46797      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46798      * (defaults to 4, does not apply if editable = false)
46799      */
46800     minChars : 4,
46801     /**
46802      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46803      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46804      */
46805     typeAhead: false,
46806     /**
46807      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46808      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46809      */
46810     queryDelay: 500,
46811     /**
46812      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46813      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46814      */
46815     pageSize: 0,
46816     /**
46817      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46818      * when editable = true (defaults to false)
46819      */
46820     selectOnFocus:false,
46821     /**
46822      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46823      */
46824     queryParam: 'query',
46825     /**
46826      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46827      * when mode = 'remote' (defaults to 'Loading...')
46828      */
46829     loadingText: 'Loading...',
46830     /**
46831      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46832      */
46833     resizable: false,
46834     /**
46835      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46836      */
46837     handleHeight : 8,
46838     /**
46839      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46840      * traditional select (defaults to true)
46841      */
46842     editable: true,
46843     /**
46844      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46845      */
46846     allQuery: '',
46847     /**
46848      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46849      */
46850     mode: 'remote',
46851     /**
46852      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46853      * listWidth has a higher value)
46854      */
46855     minListWidth : 70,
46856     /**
46857      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46858      * allow the user to set arbitrary text into the field (defaults to false)
46859      */
46860     forceSelection:false,
46861     /**
46862      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46863      * if typeAhead = true (defaults to 250)
46864      */
46865     typeAheadDelay : 250,
46866     /**
46867      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46868      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46869      */
46870     valueNotFoundText : undefined,
46871     
46872     /**
46873      * @cfg {String} defaultValue The value displayed after loading the store.
46874      */
46875     defaultValue: '',
46876     
46877     /**
46878      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46879      */
46880     blockFocus : false,
46881     
46882     /**
46883      * @cfg {Boolean} disableClear Disable showing of clear button.
46884      */
46885     disableClear : false,
46886     /**
46887      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46888      */
46889     alwaysQuery : false,
46890     
46891     //private
46892     addicon : false,
46893     editicon: false,
46894     
46895     // element that contains real text value.. (when hidden is used..)
46896      
46897     // private
46898     onRender : function(ct, position){
46899         Roo.form.Field.prototype.onRender.call(this, ct, position);
46900         
46901         if(this.store){
46902             this.store.on('beforeload', this.onBeforeLoad, this);
46903             this.store.on('load', this.onLoad, this);
46904             this.store.on('loadexception', this.onLoadException, this);
46905             this.store.load({});
46906         }
46907         
46908         
46909         
46910     },
46911
46912     // private
46913     initEvents : function(){
46914         //Roo.form.ComboBox.superclass.initEvents.call(this);
46915  
46916     },
46917
46918     onDestroy : function(){
46919        
46920         if(this.store){
46921             this.store.un('beforeload', this.onBeforeLoad, this);
46922             this.store.un('load', this.onLoad, this);
46923             this.store.un('loadexception', this.onLoadException, this);
46924         }
46925         //Roo.form.ComboBox.superclass.onDestroy.call(this);
46926     },
46927
46928     // private
46929     fireKey : function(e){
46930         if(e.isNavKeyPress() && !this.list.isVisible()){
46931             this.fireEvent("specialkey", this, e);
46932         }
46933     },
46934
46935     // private
46936     onResize: function(w, h){
46937         
46938         return; 
46939     
46940         
46941     },
46942
46943     /**
46944      * Allow or prevent the user from directly editing the field text.  If false is passed,
46945      * the user will only be able to select from the items defined in the dropdown list.  This method
46946      * is the runtime equivalent of setting the 'editable' config option at config time.
46947      * @param {Boolean} value True to allow the user to directly edit the field text
46948      */
46949     setEditable : function(value){
46950          
46951     },
46952
46953     // private
46954     onBeforeLoad : function(){
46955         
46956         Roo.log("Select before load");
46957         return;
46958     
46959         this.innerList.update(this.loadingText ?
46960                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
46961         //this.restrictHeight();
46962         this.selectedIndex = -1;
46963     },
46964
46965     // private
46966     onLoad : function(){
46967
46968     
46969         var dom = this.el.dom;
46970         dom.innerHTML = '';
46971          var od = dom.ownerDocument;
46972          
46973         if (this.emptyText) {
46974             var op = od.createElement('option');
46975             op.setAttribute('value', '');
46976             op.innerHTML = String.format('{0}', this.emptyText);
46977             dom.appendChild(op);
46978         }
46979         if(this.store.getCount() > 0){
46980            
46981             var vf = this.valueField;
46982             var df = this.displayField;
46983             this.store.data.each(function(r) {
46984                 // which colmsn to use... testing - cdoe / title..
46985                 var op = od.createElement('option');
46986                 op.setAttribute('value', r.data[vf]);
46987                 op.innerHTML = String.format('{0}', r.data[df]);
46988                 dom.appendChild(op);
46989             });
46990             if (typeof(this.defaultValue != 'undefined')) {
46991                 this.setValue(this.defaultValue);
46992             }
46993             
46994              
46995         }else{
46996             //this.onEmptyResults();
46997         }
46998         //this.el.focus();
46999     },
47000     // private
47001     onLoadException : function()
47002     {
47003         dom.innerHTML = '';
47004             
47005         Roo.log("Select on load exception");
47006         return;
47007     
47008         this.collapse();
47009         Roo.log(this.store.reader.jsonData);
47010         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47011             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47012         }
47013         
47014         
47015     },
47016     // private
47017     onTypeAhead : function(){
47018          
47019     },
47020
47021     // private
47022     onSelect : function(record, index){
47023         Roo.log('on select?');
47024         return;
47025         if(this.fireEvent('beforeselect', this, record, index) !== false){
47026             this.setFromData(index > -1 ? record.data : false);
47027             this.collapse();
47028             this.fireEvent('select', this, record, index);
47029         }
47030     },
47031
47032     /**
47033      * Returns the currently selected field value or empty string if no value is set.
47034      * @return {String} value The selected value
47035      */
47036     getValue : function(){
47037         var dom = this.el.dom;
47038         this.value = dom.options[dom.selectedIndex].value;
47039         return this.value;
47040         
47041     },
47042
47043     /**
47044      * Clears any text/value currently set in the field
47045      */
47046     clearValue : function(){
47047         this.value = '';
47048         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47049         
47050     },
47051
47052     /**
47053      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47054      * will be displayed in the field.  If the value does not match the data value of an existing item,
47055      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47056      * Otherwise the field will be blank (although the value will still be set).
47057      * @param {String} value The value to match
47058      */
47059     setValue : function(v){
47060         var d = this.el.dom;
47061         for (var i =0; i < d.options.length;i++) {
47062             if (v == d.options[i].value) {
47063                 d.selectedIndex = i;
47064                 this.value = v;
47065                 return;
47066             }
47067         }
47068         this.clearValue();
47069     },
47070     /**
47071      * @property {Object} the last set data for the element
47072      */
47073     
47074     lastData : false,
47075     /**
47076      * Sets the value of the field based on a object which is related to the record format for the store.
47077      * @param {Object} value the value to set as. or false on reset?
47078      */
47079     setFromData : function(o){
47080         Roo.log('setfrom data?');
47081          
47082         
47083         
47084     },
47085     // private
47086     reset : function(){
47087         this.clearValue();
47088     },
47089     // private
47090     findRecord : function(prop, value){
47091         
47092         return false;
47093     
47094         var record;
47095         if(this.store.getCount() > 0){
47096             this.store.each(function(r){
47097                 if(r.data[prop] == value){
47098                     record = r;
47099                     return false;
47100                 }
47101                 return true;
47102             });
47103         }
47104         return record;
47105     },
47106     
47107     getName: function()
47108     {
47109         // returns hidden if it's set..
47110         if (!this.rendered) {return ''};
47111         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47112         
47113     },
47114      
47115
47116     
47117
47118     // private
47119     onEmptyResults : function(){
47120         Roo.log('empty results');
47121         //this.collapse();
47122     },
47123
47124     /**
47125      * Returns true if the dropdown list is expanded, else false.
47126      */
47127     isExpanded : function(){
47128         return false;
47129     },
47130
47131     /**
47132      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47133      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47134      * @param {String} value The data value of the item to select
47135      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47136      * selected item if it is not currently in view (defaults to true)
47137      * @return {Boolean} True if the value matched an item in the list, else false
47138      */
47139     selectByValue : function(v, scrollIntoView){
47140         Roo.log('select By Value');
47141         return false;
47142     
47143         if(v !== undefined && v !== null){
47144             var r = this.findRecord(this.valueField || this.displayField, v);
47145             if(r){
47146                 this.select(this.store.indexOf(r), scrollIntoView);
47147                 return true;
47148             }
47149         }
47150         return false;
47151     },
47152
47153     /**
47154      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47156      * @param {Number} index The zero-based index of the list item to select
47157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47158      * selected item if it is not currently in view (defaults to true)
47159      */
47160     select : function(index, scrollIntoView){
47161         Roo.log('select ');
47162         return  ;
47163         
47164         this.selectedIndex = index;
47165         this.view.select(index);
47166         if(scrollIntoView !== false){
47167             var el = this.view.getNode(index);
47168             if(el){
47169                 this.innerList.scrollChildIntoView(el, false);
47170             }
47171         }
47172     },
47173
47174       
47175
47176     // private
47177     validateBlur : function(){
47178         
47179         return;
47180         
47181     },
47182
47183     // private
47184     initQuery : function(){
47185         this.doQuery(this.getRawValue());
47186     },
47187
47188     // private
47189     doForce : function(){
47190         if(this.el.dom.value.length > 0){
47191             this.el.dom.value =
47192                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47193              
47194         }
47195     },
47196
47197     /**
47198      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47199      * query allowing the query action to be canceled if needed.
47200      * @param {String} query The SQL query to execute
47201      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47202      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47203      * saved in the current store (defaults to false)
47204      */
47205     doQuery : function(q, forceAll){
47206         
47207         Roo.log('doQuery?');
47208         if(q === undefined || q === null){
47209             q = '';
47210         }
47211         var qe = {
47212             query: q,
47213             forceAll: forceAll,
47214             combo: this,
47215             cancel:false
47216         };
47217         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47218             return false;
47219         }
47220         q = qe.query;
47221         forceAll = qe.forceAll;
47222         if(forceAll === true || (q.length >= this.minChars)){
47223             if(this.lastQuery != q || this.alwaysQuery){
47224                 this.lastQuery = q;
47225                 if(this.mode == 'local'){
47226                     this.selectedIndex = -1;
47227                     if(forceAll){
47228                         this.store.clearFilter();
47229                     }else{
47230                         this.store.filter(this.displayField, q);
47231                     }
47232                     this.onLoad();
47233                 }else{
47234                     this.store.baseParams[this.queryParam] = q;
47235                     this.store.load({
47236                         params: this.getParams(q)
47237                     });
47238                     this.expand();
47239                 }
47240             }else{
47241                 this.selectedIndex = -1;
47242                 this.onLoad();   
47243             }
47244         }
47245     },
47246
47247     // private
47248     getParams : function(q){
47249         var p = {};
47250         //p[this.queryParam] = q;
47251         if(this.pageSize){
47252             p.start = 0;
47253             p.limit = this.pageSize;
47254         }
47255         return p;
47256     },
47257
47258     /**
47259      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47260      */
47261     collapse : function(){
47262         
47263     },
47264
47265     // private
47266     collapseIf : function(e){
47267         
47268     },
47269
47270     /**
47271      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47272      */
47273     expand : function(){
47274         
47275     } ,
47276
47277     // private
47278      
47279
47280     /** 
47281     * @cfg {Boolean} grow 
47282     * @hide 
47283     */
47284     /** 
47285     * @cfg {Number} growMin 
47286     * @hide 
47287     */
47288     /** 
47289     * @cfg {Number} growMax 
47290     * @hide 
47291     */
47292     /**
47293      * @hide
47294      * @method autoSize
47295      */
47296     
47297     setWidth : function()
47298     {
47299         
47300     },
47301     getResizeEl : function(){
47302         return this.el;
47303     }
47304 });//<script type="text/javasscript">
47305  
47306
47307 /**
47308  * @class Roo.DDView
47309  * A DnD enabled version of Roo.View.
47310  * @param {Element/String} container The Element in which to create the View.
47311  * @param {String} tpl The template string used to create the markup for each element of the View
47312  * @param {Object} config The configuration properties. These include all the config options of
47313  * {@link Roo.View} plus some specific to this class.<br>
47314  * <p>
47315  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47316  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47317  * <p>
47318  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47319 .x-view-drag-insert-above {
47320         border-top:1px dotted #3366cc;
47321 }
47322 .x-view-drag-insert-below {
47323         border-bottom:1px dotted #3366cc;
47324 }
47325 </code></pre>
47326  * 
47327  */
47328  
47329 Roo.DDView = function(container, tpl, config) {
47330     Roo.DDView.superclass.constructor.apply(this, arguments);
47331     this.getEl().setStyle("outline", "0px none");
47332     this.getEl().unselectable();
47333     if (this.dragGroup) {
47334                 this.setDraggable(this.dragGroup.split(","));
47335     }
47336     if (this.dropGroup) {
47337                 this.setDroppable(this.dropGroup.split(","));
47338     }
47339     if (this.deletable) {
47340         this.setDeletable();
47341     }
47342     this.isDirtyFlag = false;
47343         this.addEvents({
47344                 "drop" : true
47345         });
47346 };
47347
47348 Roo.extend(Roo.DDView, Roo.View, {
47349 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47350 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47351 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47352 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47353
47354         isFormField: true,
47355
47356         reset: Roo.emptyFn,
47357         
47358         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47359
47360         validate: function() {
47361                 return true;
47362         },
47363         
47364         destroy: function() {
47365                 this.purgeListeners();
47366                 this.getEl.removeAllListeners();
47367                 this.getEl().remove();
47368                 if (this.dragZone) {
47369                         if (this.dragZone.destroy) {
47370                                 this.dragZone.destroy();
47371                         }
47372                 }
47373                 if (this.dropZone) {
47374                         if (this.dropZone.destroy) {
47375                                 this.dropZone.destroy();
47376                         }
47377                 }
47378         },
47379
47380 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47381         getName: function() {
47382                 return this.name;
47383         },
47384
47385 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47386         setValue: function(v) {
47387                 if (!this.store) {
47388                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47389                 }
47390                 var data = {};
47391                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47392                 this.store.proxy = new Roo.data.MemoryProxy(data);
47393                 this.store.load();
47394         },
47395
47396 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47397         getValue: function() {
47398                 var result = '(';
47399                 this.store.each(function(rec) {
47400                         result += rec.id + ',';
47401                 });
47402                 return result.substr(0, result.length - 1) + ')';
47403         },
47404         
47405         getIds: function() {
47406                 var i = 0, result = new Array(this.store.getCount());
47407                 this.store.each(function(rec) {
47408                         result[i++] = rec.id;
47409                 });
47410                 return result;
47411         },
47412         
47413         isDirty: function() {
47414                 return this.isDirtyFlag;
47415         },
47416
47417 /**
47418  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47419  *      whole Element becomes the target, and this causes the drop gesture to append.
47420  */
47421     getTargetFromEvent : function(e) {
47422                 var target = e.getTarget();
47423                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47424                 target = target.parentNode;
47425                 }
47426                 if (!target) {
47427                         target = this.el.dom.lastChild || this.el.dom;
47428                 }
47429                 return target;
47430     },
47431
47432 /**
47433  *      Create the drag data which consists of an object which has the property "ddel" as
47434  *      the drag proxy element. 
47435  */
47436     getDragData : function(e) {
47437         var target = this.findItemFromChild(e.getTarget());
47438                 if(target) {
47439                         this.handleSelection(e);
47440                         var selNodes = this.getSelectedNodes();
47441             var dragData = {
47442                 source: this,
47443                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47444                 nodes: selNodes,
47445                 records: []
47446                         };
47447                         var selectedIndices = this.getSelectedIndexes();
47448                         for (var i = 0; i < selectedIndices.length; i++) {
47449                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47450                         }
47451                         if (selNodes.length == 1) {
47452                                 dragData.ddel = target.cloneNode(true); // the div element
47453                         } else {
47454                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47455                                 div.className = 'multi-proxy';
47456                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47457                                         div.appendChild(selNodes[i].cloneNode(true));
47458                                 }
47459                                 dragData.ddel = div;
47460                         }
47461             //console.log(dragData)
47462             //console.log(dragData.ddel.innerHTML)
47463                         return dragData;
47464                 }
47465         //console.log('nodragData')
47466                 return false;
47467     },
47468     
47469 /**     Specify to which ddGroup items in this DDView may be dragged. */
47470     setDraggable: function(ddGroup) {
47471         if (ddGroup instanceof Array) {
47472                 Roo.each(ddGroup, this.setDraggable, this);
47473                 return;
47474         }
47475         if (this.dragZone) {
47476                 this.dragZone.addToGroup(ddGroup);
47477         } else {
47478                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47479                                 containerScroll: true,
47480                                 ddGroup: ddGroup 
47481
47482                         });
47483 //                      Draggability implies selection. DragZone's mousedown selects the element.
47484                         if (!this.multiSelect) { this.singleSelect = true; }
47485
47486 //                      Wire the DragZone's handlers up to methods in *this*
47487                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47488                 }
47489     },
47490
47491 /**     Specify from which ddGroup this DDView accepts drops. */
47492     setDroppable: function(ddGroup) {
47493         if (ddGroup instanceof Array) {
47494                 Roo.each(ddGroup, this.setDroppable, this);
47495                 return;
47496         }
47497         if (this.dropZone) {
47498                 this.dropZone.addToGroup(ddGroup);
47499         } else {
47500                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47501                                 containerScroll: true,
47502                                 ddGroup: ddGroup
47503                         });
47504
47505 //                      Wire the DropZone's handlers up to methods in *this*
47506                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47507                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47508                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47509                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47510                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47511                 }
47512     },
47513
47514 /**     Decide whether to drop above or below a View node. */
47515     getDropPoint : function(e, n, dd){
47516         if (n == this.el.dom) { return "above"; }
47517                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47518                 var c = t + (b - t) / 2;
47519                 var y = Roo.lib.Event.getPageY(e);
47520                 if(y <= c) {
47521                         return "above";
47522                 }else{
47523                         return "below";
47524                 }
47525     },
47526
47527     onNodeEnter : function(n, dd, e, data){
47528                 return false;
47529     },
47530     
47531     onNodeOver : function(n, dd, e, data){
47532                 var pt = this.getDropPoint(e, n, dd);
47533                 // set the insert point style on the target node
47534                 var dragElClass = this.dropNotAllowed;
47535                 if (pt) {
47536                         var targetElClass;
47537                         if (pt == "above"){
47538                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47539                                 targetElClass = "x-view-drag-insert-above";
47540                         } else {
47541                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47542                                 targetElClass = "x-view-drag-insert-below";
47543                         }
47544                         if (this.lastInsertClass != targetElClass){
47545                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47546                                 this.lastInsertClass = targetElClass;
47547                         }
47548                 }
47549                 return dragElClass;
47550         },
47551
47552     onNodeOut : function(n, dd, e, data){
47553                 this.removeDropIndicators(n);
47554     },
47555
47556     onNodeDrop : function(n, dd, e, data){
47557         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47558                 return false;
47559         }
47560         var pt = this.getDropPoint(e, n, dd);
47561                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47562                 if (pt == "below") { insertAt++; }
47563                 for (var i = 0; i < data.records.length; i++) {
47564                         var r = data.records[i];
47565                         var dup = this.store.getById(r.id);
47566                         if (dup && (dd != this.dragZone)) {
47567                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47568                         } else {
47569                                 if (data.copy) {
47570                                         this.store.insert(insertAt++, r.copy());
47571                                 } else {
47572                                         data.source.isDirtyFlag = true;
47573                                         r.store.remove(r);
47574                                         this.store.insert(insertAt++, r);
47575                                 }
47576                                 this.isDirtyFlag = true;
47577                         }
47578                 }
47579                 this.dragZone.cachedTarget = null;
47580                 return true;
47581     },
47582
47583     removeDropIndicators : function(n){
47584                 if(n){
47585                         Roo.fly(n).removeClass([
47586                                 "x-view-drag-insert-above",
47587                                 "x-view-drag-insert-below"]);
47588                         this.lastInsertClass = "_noclass";
47589                 }
47590     },
47591
47592 /**
47593  *      Utility method. Add a delete option to the DDView's context menu.
47594  *      @param {String} imageUrl The URL of the "delete" icon image.
47595  */
47596         setDeletable: function(imageUrl) {
47597                 if (!this.singleSelect && !this.multiSelect) {
47598                         this.singleSelect = true;
47599                 }
47600                 var c = this.getContextMenu();
47601                 this.contextMenu.on("itemclick", function(item) {
47602                         switch (item.id) {
47603                                 case "delete":
47604                                         this.remove(this.getSelectedIndexes());
47605                                         break;
47606                         }
47607                 }, this);
47608                 this.contextMenu.add({
47609                         icon: imageUrl,
47610                         id: "delete",
47611                         text: 'Delete'
47612                 });
47613         },
47614         
47615 /**     Return the context menu for this DDView. */
47616         getContextMenu: function() {
47617                 if (!this.contextMenu) {
47618 //                      Create the View's context menu
47619                         this.contextMenu = new Roo.menu.Menu({
47620                                 id: this.id + "-contextmenu"
47621                         });
47622                         this.el.on("contextmenu", this.showContextMenu, this);
47623                 }
47624                 return this.contextMenu;
47625         },
47626         
47627         disableContextMenu: function() {
47628                 if (this.contextMenu) {
47629                         this.el.un("contextmenu", this.showContextMenu, this);
47630                 }
47631         },
47632
47633         showContextMenu: function(e, item) {
47634         item = this.findItemFromChild(e.getTarget());
47635                 if (item) {
47636                         e.stopEvent();
47637                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47638                         this.contextMenu.showAt(e.getXY());
47639             }
47640     },
47641
47642 /**
47643  *      Remove {@link Roo.data.Record}s at the specified indices.
47644  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47645  */
47646     remove: function(selectedIndices) {
47647                 selectedIndices = [].concat(selectedIndices);
47648                 for (var i = 0; i < selectedIndices.length; i++) {
47649                         var rec = this.store.getAt(selectedIndices[i]);
47650                         this.store.remove(rec);
47651                 }
47652     },
47653
47654 /**
47655  *      Double click fires the event, but also, if this is draggable, and there is only one other
47656  *      related DropZone, it transfers the selected node.
47657  */
47658     onDblClick : function(e){
47659         var item = this.findItemFromChild(e.getTarget());
47660         if(item){
47661             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47662                 return false;
47663             }
47664             if (this.dragGroup) {
47665                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47666                     while (targets.indexOf(this.dropZone) > -1) {
47667                             targets.remove(this.dropZone);
47668                                 }
47669                     if (targets.length == 1) {
47670                                         this.dragZone.cachedTarget = null;
47671                         var el = Roo.get(targets[0].getEl());
47672                         var box = el.getBox(true);
47673                         targets[0].onNodeDrop(el.dom, {
47674                                 target: el.dom,
47675                                 xy: [box.x, box.y + box.height - 1]
47676                         }, null, this.getDragData(e));
47677                     }
47678                 }
47679         }
47680     },
47681     
47682     handleSelection: function(e) {
47683                 this.dragZone.cachedTarget = null;
47684         var item = this.findItemFromChild(e.getTarget());
47685         if (!item) {
47686                 this.clearSelections(true);
47687                 return;
47688         }
47689                 if (item && (this.multiSelect || this.singleSelect)){
47690                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47691                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47692                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47693                                 this.unselect(item);
47694                         } else {
47695                                 this.select(item, this.multiSelect && e.ctrlKey);
47696                                 this.lastSelection = item;
47697                         }
47698                 }
47699     },
47700
47701     onItemClick : function(item, index, e){
47702                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47703                         return false;
47704                 }
47705                 return true;
47706     },
47707
47708     unselect : function(nodeInfo, suppressEvent){
47709                 var node = this.getNode(nodeInfo);
47710                 if(node && this.isSelected(node)){
47711                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47712                                 Roo.fly(node).removeClass(this.selectedClass);
47713                                 this.selections.remove(node);
47714                                 if(!suppressEvent){
47715                                         this.fireEvent("selectionchange", this, this.selections);
47716                                 }
47717                         }
47718                 }
47719     }
47720 });
47721 /*
47722  * Based on:
47723  * Ext JS Library 1.1.1
47724  * Copyright(c) 2006-2007, Ext JS, LLC.
47725  *
47726  * Originally Released Under LGPL - original licence link has changed is not relivant.
47727  *
47728  * Fork - LGPL
47729  * <script type="text/javascript">
47730  */
47731  
47732 /**
47733  * @class Roo.LayoutManager
47734  * @extends Roo.util.Observable
47735  * Base class for layout managers.
47736  */
47737 Roo.LayoutManager = function(container, config){
47738     Roo.LayoutManager.superclass.constructor.call(this);
47739     this.el = Roo.get(container);
47740     // ie scrollbar fix
47741     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47742         document.body.scroll = "no";
47743     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47744         this.el.position('relative');
47745     }
47746     this.id = this.el.id;
47747     this.el.addClass("x-layout-container");
47748     /** false to disable window resize monitoring @type Boolean */
47749     this.monitorWindowResize = true;
47750     this.regions = {};
47751     this.addEvents({
47752         /**
47753          * @event layout
47754          * Fires when a layout is performed. 
47755          * @param {Roo.LayoutManager} this
47756          */
47757         "layout" : true,
47758         /**
47759          * @event regionresized
47760          * Fires when the user resizes a region. 
47761          * @param {Roo.LayoutRegion} region The resized region
47762          * @param {Number} newSize The new size (width for east/west, height for north/south)
47763          */
47764         "regionresized" : true,
47765         /**
47766          * @event regioncollapsed
47767          * Fires when a region is collapsed. 
47768          * @param {Roo.LayoutRegion} region The collapsed region
47769          */
47770         "regioncollapsed" : true,
47771         /**
47772          * @event regionexpanded
47773          * Fires when a region is expanded.  
47774          * @param {Roo.LayoutRegion} region The expanded region
47775          */
47776         "regionexpanded" : true
47777     });
47778     this.updating = false;
47779     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47780 };
47781
47782 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47783     /**
47784      * Returns true if this layout is currently being updated
47785      * @return {Boolean}
47786      */
47787     isUpdating : function(){
47788         return this.updating; 
47789     },
47790     
47791     /**
47792      * Suspend the LayoutManager from doing auto-layouts while
47793      * making multiple add or remove calls
47794      */
47795     beginUpdate : function(){
47796         this.updating = true;    
47797     },
47798     
47799     /**
47800      * Restore auto-layouts and optionally disable the manager from performing a layout
47801      * @param {Boolean} noLayout true to disable a layout update 
47802      */
47803     endUpdate : function(noLayout){
47804         this.updating = false;
47805         if(!noLayout){
47806             this.layout();
47807         }    
47808     },
47809     
47810     layout: function(){
47811         
47812     },
47813     
47814     onRegionResized : function(region, newSize){
47815         this.fireEvent("regionresized", region, newSize);
47816         this.layout();
47817     },
47818     
47819     onRegionCollapsed : function(region){
47820         this.fireEvent("regioncollapsed", region);
47821     },
47822     
47823     onRegionExpanded : function(region){
47824         this.fireEvent("regionexpanded", region);
47825     },
47826         
47827     /**
47828      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47829      * performs box-model adjustments.
47830      * @return {Object} The size as an object {width: (the width), height: (the height)}
47831      */
47832     getViewSize : function(){
47833         var size;
47834         if(this.el.dom != document.body){
47835             size = this.el.getSize();
47836         }else{
47837             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47838         }
47839         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47840         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47841         return size;
47842     },
47843     
47844     /**
47845      * Returns the Element this layout is bound to.
47846      * @return {Roo.Element}
47847      */
47848     getEl : function(){
47849         return this.el;
47850     },
47851     
47852     /**
47853      * Returns the specified region.
47854      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47855      * @return {Roo.LayoutRegion}
47856      */
47857     getRegion : function(target){
47858         return this.regions[target.toLowerCase()];
47859     },
47860     
47861     onWindowResize : function(){
47862         if(this.monitorWindowResize){
47863             this.layout();
47864         }
47865     }
47866 });/*
47867  * Based on:
47868  * Ext JS Library 1.1.1
47869  * Copyright(c) 2006-2007, Ext JS, LLC.
47870  *
47871  * Originally Released Under LGPL - original licence link has changed is not relivant.
47872  *
47873  * Fork - LGPL
47874  * <script type="text/javascript">
47875  */
47876 /**
47877  * @class Roo.BorderLayout
47878  * @extends Roo.LayoutManager
47879  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47880  * please see: <br><br>
47881  * <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>
47882  * <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>
47883  * Example:
47884  <pre><code>
47885  var layout = new Roo.BorderLayout(document.body, {
47886     north: {
47887         initialSize: 25,
47888         titlebar: false
47889     },
47890     west: {
47891         split:true,
47892         initialSize: 200,
47893         minSize: 175,
47894         maxSize: 400,
47895         titlebar: true,
47896         collapsible: true
47897     },
47898     east: {
47899         split:true,
47900         initialSize: 202,
47901         minSize: 175,
47902         maxSize: 400,
47903         titlebar: true,
47904         collapsible: true
47905     },
47906     south: {
47907         split:true,
47908         initialSize: 100,
47909         minSize: 100,
47910         maxSize: 200,
47911         titlebar: true,
47912         collapsible: true
47913     },
47914     center: {
47915         titlebar: true,
47916         autoScroll:true,
47917         resizeTabs: true,
47918         minTabWidth: 50,
47919         preferredTabWidth: 150
47920     }
47921 });
47922
47923 // shorthand
47924 var CP = Roo.ContentPanel;
47925
47926 layout.beginUpdate();
47927 layout.add("north", new CP("north", "North"));
47928 layout.add("south", new CP("south", {title: "South", closable: true}));
47929 layout.add("west", new CP("west", {title: "West"}));
47930 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
47931 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
47932 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
47933 layout.getRegion("center").showPanel("center1");
47934 layout.endUpdate();
47935 </code></pre>
47936
47937 <b>The container the layout is rendered into can be either the body element or any other element.
47938 If it is not the body element, the container needs to either be an absolute positioned element,
47939 or you will need to add "position:relative" to the css of the container.  You will also need to specify
47940 the container size if it is not the body element.</b>
47941
47942 * @constructor
47943 * Create a new BorderLayout
47944 * @param {String/HTMLElement/Element} container The container this layout is bound to
47945 * @param {Object} config Configuration options
47946  */
47947 Roo.BorderLayout = function(container, config){
47948     config = config || {};
47949     Roo.BorderLayout.superclass.constructor.call(this, container, config);
47950     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
47951     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
47952         var target = this.factory.validRegions[i];
47953         if(config[target]){
47954             this.addRegion(target, config[target]);
47955         }
47956     }
47957 };
47958
47959 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
47960     /**
47961      * Creates and adds a new region if it doesn't already exist.
47962      * @param {String} target The target region key (north, south, east, west or center).
47963      * @param {Object} config The regions config object
47964      * @return {BorderLayoutRegion} The new region
47965      */
47966     addRegion : function(target, config){
47967         if(!this.regions[target]){
47968             var r = this.factory.create(target, this, config);
47969             this.bindRegion(target, r);
47970         }
47971         return this.regions[target];
47972     },
47973
47974     // private (kinda)
47975     bindRegion : function(name, r){
47976         this.regions[name] = r;
47977         r.on("visibilitychange", this.layout, this);
47978         r.on("paneladded", this.layout, this);
47979         r.on("panelremoved", this.layout, this);
47980         r.on("invalidated", this.layout, this);
47981         r.on("resized", this.onRegionResized, this);
47982         r.on("collapsed", this.onRegionCollapsed, this);
47983         r.on("expanded", this.onRegionExpanded, this);
47984     },
47985
47986     /**
47987      * Performs a layout update.
47988      */
47989     layout : function(){
47990         if(this.updating) return;
47991         var size = this.getViewSize();
47992         var w = size.width;
47993         var h = size.height;
47994         var centerW = w;
47995         var centerH = h;
47996         var centerY = 0;
47997         var centerX = 0;
47998         //var x = 0, y = 0;
47999
48000         var rs = this.regions;
48001         var north = rs["north"];
48002         var south = rs["south"]; 
48003         var west = rs["west"];
48004         var east = rs["east"];
48005         var center = rs["center"];
48006         //if(this.hideOnLayout){ // not supported anymore
48007             //c.el.setStyle("display", "none");
48008         //}
48009         if(north && north.isVisible()){
48010             var b = north.getBox();
48011             var m = north.getMargins();
48012             b.width = w - (m.left+m.right);
48013             b.x = m.left;
48014             b.y = m.top;
48015             centerY = b.height + b.y + m.bottom;
48016             centerH -= centerY;
48017             north.updateBox(this.safeBox(b));
48018         }
48019         if(south && south.isVisible()){
48020             var b = south.getBox();
48021             var m = south.getMargins();
48022             b.width = w - (m.left+m.right);
48023             b.x = m.left;
48024             var totalHeight = (b.height + m.top + m.bottom);
48025             b.y = h - totalHeight + m.top;
48026             centerH -= totalHeight;
48027             south.updateBox(this.safeBox(b));
48028         }
48029         if(west && west.isVisible()){
48030             var b = west.getBox();
48031             var m = west.getMargins();
48032             b.height = centerH - (m.top+m.bottom);
48033             b.x = m.left;
48034             b.y = centerY + m.top;
48035             var totalWidth = (b.width + m.left + m.right);
48036             centerX += totalWidth;
48037             centerW -= totalWidth;
48038             west.updateBox(this.safeBox(b));
48039         }
48040         if(east && east.isVisible()){
48041             var b = east.getBox();
48042             var m = east.getMargins();
48043             b.height = centerH - (m.top+m.bottom);
48044             var totalWidth = (b.width + m.left + m.right);
48045             b.x = w - totalWidth + m.left;
48046             b.y = centerY + m.top;
48047             centerW -= totalWidth;
48048             east.updateBox(this.safeBox(b));
48049         }
48050         if(center){
48051             var m = center.getMargins();
48052             var centerBox = {
48053                 x: centerX + m.left,
48054                 y: centerY + m.top,
48055                 width: centerW - (m.left+m.right),
48056                 height: centerH - (m.top+m.bottom)
48057             };
48058             //if(this.hideOnLayout){
48059                 //center.el.setStyle("display", "block");
48060             //}
48061             center.updateBox(this.safeBox(centerBox));
48062         }
48063         this.el.repaint();
48064         this.fireEvent("layout", this);
48065     },
48066
48067     // private
48068     safeBox : function(box){
48069         box.width = Math.max(0, box.width);
48070         box.height = Math.max(0, box.height);
48071         return box;
48072     },
48073
48074     /**
48075      * Adds a ContentPanel (or subclass) to this layout.
48076      * @param {String} target The target region key (north, south, east, west or center).
48077      * @param {Roo.ContentPanel} panel The panel to add
48078      * @return {Roo.ContentPanel} The added panel
48079      */
48080     add : function(target, panel){
48081          
48082         target = target.toLowerCase();
48083         return this.regions[target].add(panel);
48084     },
48085
48086     /**
48087      * Remove a ContentPanel (or subclass) to this layout.
48088      * @param {String} target The target region key (north, south, east, west or center).
48089      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48090      * @return {Roo.ContentPanel} The removed panel
48091      */
48092     remove : function(target, panel){
48093         target = target.toLowerCase();
48094         return this.regions[target].remove(panel);
48095     },
48096
48097     /**
48098      * Searches all regions for a panel with the specified id
48099      * @param {String} panelId
48100      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48101      */
48102     findPanel : function(panelId){
48103         var rs = this.regions;
48104         for(var target in rs){
48105             if(typeof rs[target] != "function"){
48106                 var p = rs[target].getPanel(panelId);
48107                 if(p){
48108                     return p;
48109                 }
48110             }
48111         }
48112         return null;
48113     },
48114
48115     /**
48116      * Searches all regions for a panel with the specified id and activates (shows) it.
48117      * @param {String/ContentPanel} panelId The panels id or the panel itself
48118      * @return {Roo.ContentPanel} The shown panel or null
48119      */
48120     showPanel : function(panelId) {
48121       var rs = this.regions;
48122       for(var target in rs){
48123          var r = rs[target];
48124          if(typeof r != "function"){
48125             if(r.hasPanel(panelId)){
48126                return r.showPanel(panelId);
48127             }
48128          }
48129       }
48130       return null;
48131    },
48132
48133    /**
48134      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48135      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48136      */
48137     restoreState : function(provider){
48138         if(!provider){
48139             provider = Roo.state.Manager;
48140         }
48141         var sm = new Roo.LayoutStateManager();
48142         sm.init(this, provider);
48143     },
48144
48145     /**
48146      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48147      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48148      * a valid ContentPanel config object.  Example:
48149      * <pre><code>
48150 // Create the main layout
48151 var layout = new Roo.BorderLayout('main-ct', {
48152     west: {
48153         split:true,
48154         minSize: 175,
48155         titlebar: true
48156     },
48157     center: {
48158         title:'Components'
48159     }
48160 }, 'main-ct');
48161
48162 // Create and add multiple ContentPanels at once via configs
48163 layout.batchAdd({
48164    west: {
48165        id: 'source-files',
48166        autoCreate:true,
48167        title:'Ext Source Files',
48168        autoScroll:true,
48169        fitToFrame:true
48170    },
48171    center : {
48172        el: cview,
48173        autoScroll:true,
48174        fitToFrame:true,
48175        toolbar: tb,
48176        resizeEl:'cbody'
48177    }
48178 });
48179 </code></pre>
48180      * @param {Object} regions An object containing ContentPanel configs by region name
48181      */
48182     batchAdd : function(regions){
48183         this.beginUpdate();
48184         for(var rname in regions){
48185             var lr = this.regions[rname];
48186             if(lr){
48187                 this.addTypedPanels(lr, regions[rname]);
48188             }
48189         }
48190         this.endUpdate();
48191     },
48192
48193     // private
48194     addTypedPanels : function(lr, ps){
48195         if(typeof ps == 'string'){
48196             lr.add(new Roo.ContentPanel(ps));
48197         }
48198         else if(ps instanceof Array){
48199             for(var i =0, len = ps.length; i < len; i++){
48200                 this.addTypedPanels(lr, ps[i]);
48201             }
48202         }
48203         else if(!ps.events){ // raw config?
48204             var el = ps.el;
48205             delete ps.el; // prevent conflict
48206             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48207         }
48208         else {  // panel object assumed!
48209             lr.add(ps);
48210         }
48211     },
48212     /**
48213      * Adds a xtype elements to the layout.
48214      * <pre><code>
48215
48216 layout.addxtype({
48217        xtype : 'ContentPanel',
48218        region: 'west',
48219        items: [ .... ]
48220    }
48221 );
48222
48223 layout.addxtype({
48224         xtype : 'NestedLayoutPanel',
48225         region: 'west',
48226         layout: {
48227            center: { },
48228            west: { }   
48229         },
48230         items : [ ... list of content panels or nested layout panels.. ]
48231    }
48232 );
48233 </code></pre>
48234      * @param {Object} cfg Xtype definition of item to add.
48235      */
48236     addxtype : function(cfg)
48237     {
48238         // basically accepts a pannel...
48239         // can accept a layout region..!?!?
48240         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48241         
48242         if (!cfg.xtype.match(/Panel$/)) {
48243             return false;
48244         }
48245         var ret = false;
48246         
48247         if (typeof(cfg.region) == 'undefined') {
48248             Roo.log("Failed to add Panel, region was not set");
48249             Roo.log(cfg);
48250             return false;
48251         }
48252         var region = cfg.region;
48253         delete cfg.region;
48254         
48255           
48256         var xitems = [];
48257         if (cfg.items) {
48258             xitems = cfg.items;
48259             delete cfg.items;
48260         }
48261         var nb = false;
48262         
48263         switch(cfg.xtype) 
48264         {
48265             case 'ContentPanel':  // ContentPanel (el, cfg)
48266             case 'ScrollPanel':  // ContentPanel (el, cfg)
48267             case 'ViewPanel': 
48268                 if(cfg.autoCreate) {
48269                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48270                 } else {
48271                     var el = this.el.createChild();
48272                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48273                 }
48274                 
48275                 this.add(region, ret);
48276                 break;
48277             
48278             
48279             case 'TreePanel': // our new panel!
48280                 cfg.el = this.el.createChild();
48281                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48282                 this.add(region, ret);
48283                 break;
48284             
48285             case 'NestedLayoutPanel': 
48286                 // create a new Layout (which is  a Border Layout...
48287                 var el = this.el.createChild();
48288                 var clayout = cfg.layout;
48289                 delete cfg.layout;
48290                 clayout.items   = clayout.items  || [];
48291                 // replace this exitems with the clayout ones..
48292                 xitems = clayout.items;
48293                  
48294                 
48295                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48296                     cfg.background = false;
48297                 }
48298                 var layout = new Roo.BorderLayout(el, clayout);
48299                 
48300                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48301                 //console.log('adding nested layout panel '  + cfg.toSource());
48302                 this.add(region, ret);
48303                 nb = {}; /// find first...
48304                 break;
48305                 
48306             case 'GridPanel': 
48307             
48308                 // needs grid and region
48309                 
48310                 //var el = this.getRegion(region).el.createChild();
48311                 var el = this.el.createChild();
48312                 // create the grid first...
48313                 
48314                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48315                 delete cfg.grid;
48316                 if (region == 'center' && this.active ) {
48317                     cfg.background = false;
48318                 }
48319                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48320                 
48321                 this.add(region, ret);
48322                 if (cfg.background) {
48323                     ret.on('activate', function(gp) {
48324                         if (!gp.grid.rendered) {
48325                             gp.grid.render();
48326                         }
48327                     });
48328                 } else {
48329                     grid.render();
48330                 }
48331                 break;
48332            
48333            
48334            
48335                 
48336                 
48337                 
48338             default: 
48339                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48340                 return null;
48341              // GridPanel (grid, cfg)
48342             
48343         }
48344         this.beginUpdate();
48345         // add children..
48346         var region = '';
48347         var abn = {};
48348         Roo.each(xitems, function(i)  {
48349             region = nb && i.region ? i.region : false;
48350             
48351             var add = ret.addxtype(i);
48352            
48353             if (region) {
48354                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48355                 if (!i.background) {
48356                     abn[region] = nb[region] ;
48357                 }
48358             }
48359             
48360         });
48361         this.endUpdate();
48362
48363         // make the last non-background panel active..
48364         //if (nb) { Roo.log(abn); }
48365         if (nb) {
48366             
48367             for(var r in abn) {
48368                 region = this.getRegion(r);
48369                 if (region) {
48370                     // tried using nb[r], but it does not work..
48371                      
48372                     region.showPanel(abn[r]);
48373                    
48374                 }
48375             }
48376         }
48377         return ret;
48378         
48379     }
48380 });
48381
48382 /**
48383  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48384  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48385  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48386  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48387  * <pre><code>
48388 // shorthand
48389 var CP = Roo.ContentPanel;
48390
48391 var layout = Roo.BorderLayout.create({
48392     north: {
48393         initialSize: 25,
48394         titlebar: false,
48395         panels: [new CP("north", "North")]
48396     },
48397     west: {
48398         split:true,
48399         initialSize: 200,
48400         minSize: 175,
48401         maxSize: 400,
48402         titlebar: true,
48403         collapsible: true,
48404         panels: [new CP("west", {title: "West"})]
48405     },
48406     east: {
48407         split:true,
48408         initialSize: 202,
48409         minSize: 175,
48410         maxSize: 400,
48411         titlebar: true,
48412         collapsible: true,
48413         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48414     },
48415     south: {
48416         split:true,
48417         initialSize: 100,
48418         minSize: 100,
48419         maxSize: 200,
48420         titlebar: true,
48421         collapsible: true,
48422         panels: [new CP("south", {title: "South", closable: true})]
48423     },
48424     center: {
48425         titlebar: true,
48426         autoScroll:true,
48427         resizeTabs: true,
48428         minTabWidth: 50,
48429         preferredTabWidth: 150,
48430         panels: [
48431             new CP("center1", {title: "Close Me", closable: true}),
48432             new CP("center2", {title: "Center Panel", closable: false})
48433         ]
48434     }
48435 }, document.body);
48436
48437 layout.getRegion("center").showPanel("center1");
48438 </code></pre>
48439  * @param config
48440  * @param targetEl
48441  */
48442 Roo.BorderLayout.create = function(config, targetEl){
48443     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48444     layout.beginUpdate();
48445     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48446     for(var j = 0, jlen = regions.length; j < jlen; j++){
48447         var lr = regions[j];
48448         if(layout.regions[lr] && config[lr].panels){
48449             var r = layout.regions[lr];
48450             var ps = config[lr].panels;
48451             layout.addTypedPanels(r, ps);
48452         }
48453     }
48454     layout.endUpdate();
48455     return layout;
48456 };
48457
48458 // private
48459 Roo.BorderLayout.RegionFactory = {
48460     // private
48461     validRegions : ["north","south","east","west","center"],
48462
48463     // private
48464     create : function(target, mgr, config){
48465         target = target.toLowerCase();
48466         if(config.lightweight || config.basic){
48467             return new Roo.BasicLayoutRegion(mgr, config, target);
48468         }
48469         switch(target){
48470             case "north":
48471                 return new Roo.NorthLayoutRegion(mgr, config);
48472             case "south":
48473                 return new Roo.SouthLayoutRegion(mgr, config);
48474             case "east":
48475                 return new Roo.EastLayoutRegion(mgr, config);
48476             case "west":
48477                 return new Roo.WestLayoutRegion(mgr, config);
48478             case "center":
48479                 return new Roo.CenterLayoutRegion(mgr, config);
48480         }
48481         throw 'Layout region "'+target+'" not supported.';
48482     }
48483 };/*
48484  * Based on:
48485  * Ext JS Library 1.1.1
48486  * Copyright(c) 2006-2007, Ext JS, LLC.
48487  *
48488  * Originally Released Under LGPL - original licence link has changed is not relivant.
48489  *
48490  * Fork - LGPL
48491  * <script type="text/javascript">
48492  */
48493  
48494 /**
48495  * @class Roo.BasicLayoutRegion
48496  * @extends Roo.util.Observable
48497  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48498  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48499  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48500  */
48501 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48502     this.mgr = mgr;
48503     this.position  = pos;
48504     this.events = {
48505         /**
48506          * @scope Roo.BasicLayoutRegion
48507          */
48508         
48509         /**
48510          * @event beforeremove
48511          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48512          * @param {Roo.LayoutRegion} this
48513          * @param {Roo.ContentPanel} panel The panel
48514          * @param {Object} e The cancel event object
48515          */
48516         "beforeremove" : true,
48517         /**
48518          * @event invalidated
48519          * Fires when the layout for this region is changed.
48520          * @param {Roo.LayoutRegion} this
48521          */
48522         "invalidated" : true,
48523         /**
48524          * @event visibilitychange
48525          * Fires when this region is shown or hidden 
48526          * @param {Roo.LayoutRegion} this
48527          * @param {Boolean} visibility true or false
48528          */
48529         "visibilitychange" : true,
48530         /**
48531          * @event paneladded
48532          * Fires when a panel is added. 
48533          * @param {Roo.LayoutRegion} this
48534          * @param {Roo.ContentPanel} panel The panel
48535          */
48536         "paneladded" : true,
48537         /**
48538          * @event panelremoved
48539          * Fires when a panel is removed. 
48540          * @param {Roo.LayoutRegion} this
48541          * @param {Roo.ContentPanel} panel The panel
48542          */
48543         "panelremoved" : true,
48544         /**
48545          * @event collapsed
48546          * Fires when this region is collapsed.
48547          * @param {Roo.LayoutRegion} this
48548          */
48549         "collapsed" : true,
48550         /**
48551          * @event expanded
48552          * Fires when this region is expanded.
48553          * @param {Roo.LayoutRegion} this
48554          */
48555         "expanded" : true,
48556         /**
48557          * @event slideshow
48558          * Fires when this region is slid into view.
48559          * @param {Roo.LayoutRegion} this
48560          */
48561         "slideshow" : true,
48562         /**
48563          * @event slidehide
48564          * Fires when this region slides out of view. 
48565          * @param {Roo.LayoutRegion} this
48566          */
48567         "slidehide" : true,
48568         /**
48569          * @event panelactivated
48570          * Fires when a panel is activated. 
48571          * @param {Roo.LayoutRegion} this
48572          * @param {Roo.ContentPanel} panel The activated panel
48573          */
48574         "panelactivated" : true,
48575         /**
48576          * @event resized
48577          * Fires when the user resizes this region. 
48578          * @param {Roo.LayoutRegion} this
48579          * @param {Number} newSize The new size (width for east/west, height for north/south)
48580          */
48581         "resized" : true
48582     };
48583     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48584     this.panels = new Roo.util.MixedCollection();
48585     this.panels.getKey = this.getPanelId.createDelegate(this);
48586     this.box = null;
48587     this.activePanel = null;
48588     // ensure listeners are added...
48589     
48590     if (config.listeners || config.events) {
48591         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48592             listeners : config.listeners || {},
48593             events : config.events || {}
48594         });
48595     }
48596     
48597     if(skipConfig !== true){
48598         this.applyConfig(config);
48599     }
48600 };
48601
48602 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48603     getPanelId : function(p){
48604         return p.getId();
48605     },
48606     
48607     applyConfig : function(config){
48608         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48609         this.config = config;
48610         
48611     },
48612     
48613     /**
48614      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48615      * the width, for horizontal (north, south) the height.
48616      * @param {Number} newSize The new width or height
48617      */
48618     resizeTo : function(newSize){
48619         var el = this.el ? this.el :
48620                  (this.activePanel ? this.activePanel.getEl() : null);
48621         if(el){
48622             switch(this.position){
48623                 case "east":
48624                 case "west":
48625                     el.setWidth(newSize);
48626                     this.fireEvent("resized", this, newSize);
48627                 break;
48628                 case "north":
48629                 case "south":
48630                     el.setHeight(newSize);
48631                     this.fireEvent("resized", this, newSize);
48632                 break;                
48633             }
48634         }
48635     },
48636     
48637     getBox : function(){
48638         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48639     },
48640     
48641     getMargins : function(){
48642         return this.margins;
48643     },
48644     
48645     updateBox : function(box){
48646         this.box = box;
48647         var el = this.activePanel.getEl();
48648         el.dom.style.left = box.x + "px";
48649         el.dom.style.top = box.y + "px";
48650         this.activePanel.setSize(box.width, box.height);
48651     },
48652     
48653     /**
48654      * Returns the container element for this region.
48655      * @return {Roo.Element}
48656      */
48657     getEl : function(){
48658         return this.activePanel;
48659     },
48660     
48661     /**
48662      * Returns true if this region is currently visible.
48663      * @return {Boolean}
48664      */
48665     isVisible : function(){
48666         return this.activePanel ? true : false;
48667     },
48668     
48669     setActivePanel : function(panel){
48670         panel = this.getPanel(panel);
48671         if(this.activePanel && this.activePanel != panel){
48672             this.activePanel.setActiveState(false);
48673             this.activePanel.getEl().setLeftTop(-10000,-10000);
48674         }
48675         this.activePanel = panel;
48676         panel.setActiveState(true);
48677         if(this.box){
48678             panel.setSize(this.box.width, this.box.height);
48679         }
48680         this.fireEvent("panelactivated", this, panel);
48681         this.fireEvent("invalidated");
48682     },
48683     
48684     /**
48685      * Show the specified panel.
48686      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48687      * @return {Roo.ContentPanel} The shown panel or null
48688      */
48689     showPanel : function(panel){
48690         if(panel = this.getPanel(panel)){
48691             this.setActivePanel(panel);
48692         }
48693         return panel;
48694     },
48695     
48696     /**
48697      * Get the active panel for this region.
48698      * @return {Roo.ContentPanel} The active panel or null
48699      */
48700     getActivePanel : function(){
48701         return this.activePanel;
48702     },
48703     
48704     /**
48705      * Add the passed ContentPanel(s)
48706      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48707      * @return {Roo.ContentPanel} The panel added (if only one was added)
48708      */
48709     add : function(panel){
48710         if(arguments.length > 1){
48711             for(var i = 0, len = arguments.length; i < len; i++) {
48712                 this.add(arguments[i]);
48713             }
48714             return null;
48715         }
48716         if(this.hasPanel(panel)){
48717             this.showPanel(panel);
48718             return panel;
48719         }
48720         var el = panel.getEl();
48721         if(el.dom.parentNode != this.mgr.el.dom){
48722             this.mgr.el.dom.appendChild(el.dom);
48723         }
48724         if(panel.setRegion){
48725             panel.setRegion(this);
48726         }
48727         this.panels.add(panel);
48728         el.setStyle("position", "absolute");
48729         if(!panel.background){
48730             this.setActivePanel(panel);
48731             if(this.config.initialSize && this.panels.getCount()==1){
48732                 this.resizeTo(this.config.initialSize);
48733             }
48734         }
48735         this.fireEvent("paneladded", this, panel);
48736         return panel;
48737     },
48738     
48739     /**
48740      * Returns true if the panel is in this region.
48741      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48742      * @return {Boolean}
48743      */
48744     hasPanel : function(panel){
48745         if(typeof panel == "object"){ // must be panel obj
48746             panel = panel.getId();
48747         }
48748         return this.getPanel(panel) ? true : false;
48749     },
48750     
48751     /**
48752      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48753      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48754      * @param {Boolean} preservePanel Overrides the config preservePanel option
48755      * @return {Roo.ContentPanel} The panel that was removed
48756      */
48757     remove : function(panel, preservePanel){
48758         panel = this.getPanel(panel);
48759         if(!panel){
48760             return null;
48761         }
48762         var e = {};
48763         this.fireEvent("beforeremove", this, panel, e);
48764         if(e.cancel === true){
48765             return null;
48766         }
48767         var panelId = panel.getId();
48768         this.panels.removeKey(panelId);
48769         return panel;
48770     },
48771     
48772     /**
48773      * Returns the panel specified or null if it's not in this region.
48774      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48775      * @return {Roo.ContentPanel}
48776      */
48777     getPanel : function(id){
48778         if(typeof id == "object"){ // must be panel obj
48779             return id;
48780         }
48781         return this.panels.get(id);
48782     },
48783     
48784     /**
48785      * Returns this regions position (north/south/east/west/center).
48786      * @return {String} 
48787      */
48788     getPosition: function(){
48789         return this.position;    
48790     }
48791 });/*
48792  * Based on:
48793  * Ext JS Library 1.1.1
48794  * Copyright(c) 2006-2007, Ext JS, LLC.
48795  *
48796  * Originally Released Under LGPL - original licence link has changed is not relivant.
48797  *
48798  * Fork - LGPL
48799  * <script type="text/javascript">
48800  */
48801  
48802 /**
48803  * @class Roo.LayoutRegion
48804  * @extends Roo.BasicLayoutRegion
48805  * This class represents a region in a layout manager.
48806  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48807  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48808  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48809  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48810  * @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})
48811  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48812  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48813  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48814  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48815  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48816  * @cfg {String}    title           The title for the region (overrides panel titles)
48817  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48818  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48819  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48820  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48821  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48822  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48823  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48824  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48825  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48826  * @cfg {Boolean}   showPin         True to show a pin button
48827  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48828  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48829  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48830  * @cfg {Number}    width           For East/West panels
48831  * @cfg {Number}    height          For North/South panels
48832  * @cfg {Boolean}   split           To show the splitter
48833  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48834  */
48835 Roo.LayoutRegion = function(mgr, config, pos){
48836     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48837     var dh = Roo.DomHelper;
48838     /** This region's container element 
48839     * @type Roo.Element */
48840     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48841     /** This region's title element 
48842     * @type Roo.Element */
48843
48844     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48845         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48846         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48847     ]}, true);
48848     this.titleEl.enableDisplayMode();
48849     /** This region's title text element 
48850     * @type HTMLElement */
48851     this.titleTextEl = this.titleEl.dom.firstChild;
48852     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48853     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48854     this.closeBtn.enableDisplayMode();
48855     this.closeBtn.on("click", this.closeClicked, this);
48856     this.closeBtn.hide();
48857
48858     this.createBody(config);
48859     this.visible = true;
48860     this.collapsed = false;
48861
48862     if(config.hideWhenEmpty){
48863         this.hide();
48864         this.on("paneladded", this.validateVisibility, this);
48865         this.on("panelremoved", this.validateVisibility, this);
48866     }
48867     this.applyConfig(config);
48868 };
48869
48870 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48871
48872     createBody : function(){
48873         /** This region's body element 
48874         * @type Roo.Element */
48875         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48876     },
48877
48878     applyConfig : function(c){
48879         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48880             var dh = Roo.DomHelper;
48881             if(c.titlebar !== false){
48882                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48883                 this.collapseBtn.on("click", this.collapse, this);
48884                 this.collapseBtn.enableDisplayMode();
48885
48886                 if(c.showPin === true || this.showPin){
48887                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48888                     this.stickBtn.enableDisplayMode();
48889                     this.stickBtn.on("click", this.expand, this);
48890                     this.stickBtn.hide();
48891                 }
48892             }
48893             /** This region's collapsed element
48894             * @type Roo.Element */
48895             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48896                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48897             ]}, true);
48898             if(c.floatable !== false){
48899                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48900                this.collapsedEl.on("click", this.collapseClick, this);
48901             }
48902
48903             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48904                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48905                    id: "message", unselectable: "on", style:{"float":"left"}});
48906                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
48907              }
48908             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
48909             this.expandBtn.on("click", this.expand, this);
48910         }
48911         if(this.collapseBtn){
48912             this.collapseBtn.setVisible(c.collapsible == true);
48913         }
48914         this.cmargins = c.cmargins || this.cmargins ||
48915                          (this.position == "west" || this.position == "east" ?
48916                              {top: 0, left: 2, right:2, bottom: 0} :
48917                              {top: 2, left: 0, right:0, bottom: 2});
48918         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48919         this.bottomTabs = c.tabPosition != "top";
48920         this.autoScroll = c.autoScroll || false;
48921         if(this.autoScroll){
48922             this.bodyEl.setStyle("overflow", "auto");
48923         }else{
48924             this.bodyEl.setStyle("overflow", "hidden");
48925         }
48926         //if(c.titlebar !== false){
48927             if((!c.titlebar && !c.title) || c.titlebar === false){
48928                 this.titleEl.hide();
48929             }else{
48930                 this.titleEl.show();
48931                 if(c.title){
48932                     this.titleTextEl.innerHTML = c.title;
48933                 }
48934             }
48935         //}
48936         this.duration = c.duration || .30;
48937         this.slideDuration = c.slideDuration || .45;
48938         this.config = c;
48939         if(c.collapsed){
48940             this.collapse(true);
48941         }
48942         if(c.hidden){
48943             this.hide();
48944         }
48945     },
48946     /**
48947      * Returns true if this region is currently visible.
48948      * @return {Boolean}
48949      */
48950     isVisible : function(){
48951         return this.visible;
48952     },
48953
48954     /**
48955      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
48956      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
48957      */
48958     setCollapsedTitle : function(title){
48959         title = title || "&#160;";
48960         if(this.collapsedTitleTextEl){
48961             this.collapsedTitleTextEl.innerHTML = title;
48962         }
48963     },
48964
48965     getBox : function(){
48966         var b;
48967         if(!this.collapsed){
48968             b = this.el.getBox(false, true);
48969         }else{
48970             b = this.collapsedEl.getBox(false, true);
48971         }
48972         return b;
48973     },
48974
48975     getMargins : function(){
48976         return this.collapsed ? this.cmargins : this.margins;
48977     },
48978
48979     highlight : function(){
48980         this.el.addClass("x-layout-panel-dragover");
48981     },
48982
48983     unhighlight : function(){
48984         this.el.removeClass("x-layout-panel-dragover");
48985     },
48986
48987     updateBox : function(box){
48988         this.box = box;
48989         if(!this.collapsed){
48990             this.el.dom.style.left = box.x + "px";
48991             this.el.dom.style.top = box.y + "px";
48992             this.updateBody(box.width, box.height);
48993         }else{
48994             this.collapsedEl.dom.style.left = box.x + "px";
48995             this.collapsedEl.dom.style.top = box.y + "px";
48996             this.collapsedEl.setSize(box.width, box.height);
48997         }
48998         if(this.tabs){
48999             this.tabs.autoSizeTabs();
49000         }
49001     },
49002
49003     updateBody : function(w, h){
49004         if(w !== null){
49005             this.el.setWidth(w);
49006             w -= this.el.getBorderWidth("rl");
49007             if(this.config.adjustments){
49008                 w += this.config.adjustments[0];
49009             }
49010         }
49011         if(h !== null){
49012             this.el.setHeight(h);
49013             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49014             h -= this.el.getBorderWidth("tb");
49015             if(this.config.adjustments){
49016                 h += this.config.adjustments[1];
49017             }
49018             this.bodyEl.setHeight(h);
49019             if(this.tabs){
49020                 h = this.tabs.syncHeight(h);
49021             }
49022         }
49023         if(this.panelSize){
49024             w = w !== null ? w : this.panelSize.width;
49025             h = h !== null ? h : this.panelSize.height;
49026         }
49027         if(this.activePanel){
49028             var el = this.activePanel.getEl();
49029             w = w !== null ? w : el.getWidth();
49030             h = h !== null ? h : el.getHeight();
49031             this.panelSize = {width: w, height: h};
49032             this.activePanel.setSize(w, h);
49033         }
49034         if(Roo.isIE && this.tabs){
49035             this.tabs.el.repaint();
49036         }
49037     },
49038
49039     /**
49040      * Returns the container element for this region.
49041      * @return {Roo.Element}
49042      */
49043     getEl : function(){
49044         return this.el;
49045     },
49046
49047     /**
49048      * Hides this region.
49049      */
49050     hide : function(){
49051         if(!this.collapsed){
49052             this.el.dom.style.left = "-2000px";
49053             this.el.hide();
49054         }else{
49055             this.collapsedEl.dom.style.left = "-2000px";
49056             this.collapsedEl.hide();
49057         }
49058         this.visible = false;
49059         this.fireEvent("visibilitychange", this, false);
49060     },
49061
49062     /**
49063      * Shows this region if it was previously hidden.
49064      */
49065     show : function(){
49066         if(!this.collapsed){
49067             this.el.show();
49068         }else{
49069             this.collapsedEl.show();
49070         }
49071         this.visible = true;
49072         this.fireEvent("visibilitychange", this, true);
49073     },
49074
49075     closeClicked : function(){
49076         if(this.activePanel){
49077             this.remove(this.activePanel);
49078         }
49079     },
49080
49081     collapseClick : function(e){
49082         if(this.isSlid){
49083            e.stopPropagation();
49084            this.slideIn();
49085         }else{
49086            e.stopPropagation();
49087            this.slideOut();
49088         }
49089     },
49090
49091     /**
49092      * Collapses this region.
49093      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49094      */
49095     collapse : function(skipAnim){
49096         if(this.collapsed) return;
49097         this.collapsed = true;
49098         if(this.split){
49099             this.split.el.hide();
49100         }
49101         if(this.config.animate && skipAnim !== true){
49102             this.fireEvent("invalidated", this);
49103             this.animateCollapse();
49104         }else{
49105             this.el.setLocation(-20000,-20000);
49106             this.el.hide();
49107             this.collapsedEl.show();
49108             this.fireEvent("collapsed", this);
49109             this.fireEvent("invalidated", this);
49110         }
49111     },
49112
49113     animateCollapse : function(){
49114         // overridden
49115     },
49116
49117     /**
49118      * Expands this region if it was previously collapsed.
49119      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49120      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49121      */
49122     expand : function(e, skipAnim){
49123         if(e) e.stopPropagation();
49124         if(!this.collapsed || this.el.hasActiveFx()) return;
49125         if(this.isSlid){
49126             this.afterSlideIn();
49127             skipAnim = true;
49128         }
49129         this.collapsed = false;
49130         if(this.config.animate && skipAnim !== true){
49131             this.animateExpand();
49132         }else{
49133             this.el.show();
49134             if(this.split){
49135                 this.split.el.show();
49136             }
49137             this.collapsedEl.setLocation(-2000,-2000);
49138             this.collapsedEl.hide();
49139             this.fireEvent("invalidated", this);
49140             this.fireEvent("expanded", this);
49141         }
49142     },
49143
49144     animateExpand : function(){
49145         // overridden
49146     },
49147
49148     initTabs : function()
49149     {
49150         this.bodyEl.setStyle("overflow", "hidden");
49151         var ts = new Roo.TabPanel(
49152                 this.bodyEl.dom,
49153                 {
49154                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49155                     disableTooltips: this.config.disableTabTips,
49156                     toolbar : this.config.toolbar
49157                 }
49158         );
49159         if(this.config.hideTabs){
49160             ts.stripWrap.setDisplayed(false);
49161         }
49162         this.tabs = ts;
49163         ts.resizeTabs = this.config.resizeTabs === true;
49164         ts.minTabWidth = this.config.minTabWidth || 40;
49165         ts.maxTabWidth = this.config.maxTabWidth || 250;
49166         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49167         ts.monitorResize = false;
49168         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49169         ts.bodyEl.addClass('x-layout-tabs-body');
49170         this.panels.each(this.initPanelAsTab, this);
49171     },
49172
49173     initPanelAsTab : function(panel){
49174         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49175                     this.config.closeOnTab && panel.isClosable());
49176         if(panel.tabTip !== undefined){
49177             ti.setTooltip(panel.tabTip);
49178         }
49179         ti.on("activate", function(){
49180               this.setActivePanel(panel);
49181         }, this);
49182         if(this.config.closeOnTab){
49183             ti.on("beforeclose", function(t, e){
49184                 e.cancel = true;
49185                 this.remove(panel);
49186             }, this);
49187         }
49188         return ti;
49189     },
49190
49191     updatePanelTitle : function(panel, title){
49192         if(this.activePanel == panel){
49193             this.updateTitle(title);
49194         }
49195         if(this.tabs){
49196             var ti = this.tabs.getTab(panel.getEl().id);
49197             ti.setText(title);
49198             if(panel.tabTip !== undefined){
49199                 ti.setTooltip(panel.tabTip);
49200             }
49201         }
49202     },
49203
49204     updateTitle : function(title){
49205         if(this.titleTextEl && !this.config.title){
49206             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49207         }
49208     },
49209
49210     setActivePanel : function(panel){
49211         panel = this.getPanel(panel);
49212         if(this.activePanel && this.activePanel != panel){
49213             this.activePanel.setActiveState(false);
49214         }
49215         this.activePanel = panel;
49216         panel.setActiveState(true);
49217         if(this.panelSize){
49218             panel.setSize(this.panelSize.width, this.panelSize.height);
49219         }
49220         if(this.closeBtn){
49221             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49222         }
49223         this.updateTitle(panel.getTitle());
49224         if(this.tabs){
49225             this.fireEvent("invalidated", this);
49226         }
49227         this.fireEvent("panelactivated", this, panel);
49228     },
49229
49230     /**
49231      * Shows the specified panel.
49232      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49233      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49234      */
49235     showPanel : function(panel){
49236         if(panel = this.getPanel(panel)){
49237             if(this.tabs){
49238                 var tab = this.tabs.getTab(panel.getEl().id);
49239                 if(tab.isHidden()){
49240                     this.tabs.unhideTab(tab.id);
49241                 }
49242                 tab.activate();
49243             }else{
49244                 this.setActivePanel(panel);
49245             }
49246         }
49247         return panel;
49248     },
49249
49250     /**
49251      * Get the active panel for this region.
49252      * @return {Roo.ContentPanel} The active panel or null
49253      */
49254     getActivePanel : function(){
49255         return this.activePanel;
49256     },
49257
49258     validateVisibility : function(){
49259         if(this.panels.getCount() < 1){
49260             this.updateTitle("&#160;");
49261             this.closeBtn.hide();
49262             this.hide();
49263         }else{
49264             if(!this.isVisible()){
49265                 this.show();
49266             }
49267         }
49268     },
49269
49270     /**
49271      * Adds the passed ContentPanel(s) to this region.
49272      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49273      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49274      */
49275     add : function(panel){
49276         if(arguments.length > 1){
49277             for(var i = 0, len = arguments.length; i < len; i++) {
49278                 this.add(arguments[i]);
49279             }
49280             return null;
49281         }
49282         if(this.hasPanel(panel)){
49283             this.showPanel(panel);
49284             return panel;
49285         }
49286         panel.setRegion(this);
49287         this.panels.add(panel);
49288         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49289             this.bodyEl.dom.appendChild(panel.getEl().dom);
49290             if(panel.background !== true){
49291                 this.setActivePanel(panel);
49292             }
49293             this.fireEvent("paneladded", this, panel);
49294             return panel;
49295         }
49296         if(!this.tabs){
49297             this.initTabs();
49298         }else{
49299             this.initPanelAsTab(panel);
49300         }
49301         if(panel.background !== true){
49302             this.tabs.activate(panel.getEl().id);
49303         }
49304         this.fireEvent("paneladded", this, panel);
49305         return panel;
49306     },
49307
49308     /**
49309      * Hides the tab for the specified panel.
49310      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49311      */
49312     hidePanel : function(panel){
49313         if(this.tabs && (panel = this.getPanel(panel))){
49314             this.tabs.hideTab(panel.getEl().id);
49315         }
49316     },
49317
49318     /**
49319      * Unhides the tab for a previously hidden panel.
49320      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49321      */
49322     unhidePanel : function(panel){
49323         if(this.tabs && (panel = this.getPanel(panel))){
49324             this.tabs.unhideTab(panel.getEl().id);
49325         }
49326     },
49327
49328     clearPanels : function(){
49329         while(this.panels.getCount() > 0){
49330              this.remove(this.panels.first());
49331         }
49332     },
49333
49334     /**
49335      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49336      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49337      * @param {Boolean} preservePanel Overrides the config preservePanel option
49338      * @return {Roo.ContentPanel} The panel that was removed
49339      */
49340     remove : function(panel, preservePanel){
49341         panel = this.getPanel(panel);
49342         if(!panel){
49343             return null;
49344         }
49345         var e = {};
49346         this.fireEvent("beforeremove", this, panel, e);
49347         if(e.cancel === true){
49348             return null;
49349         }
49350         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49351         var panelId = panel.getId();
49352         this.panels.removeKey(panelId);
49353         if(preservePanel){
49354             document.body.appendChild(panel.getEl().dom);
49355         }
49356         if(this.tabs){
49357             this.tabs.removeTab(panel.getEl().id);
49358         }else if (!preservePanel){
49359             this.bodyEl.dom.removeChild(panel.getEl().dom);
49360         }
49361         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49362             var p = this.panels.first();
49363             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49364             tempEl.appendChild(p.getEl().dom);
49365             this.bodyEl.update("");
49366             this.bodyEl.dom.appendChild(p.getEl().dom);
49367             tempEl = null;
49368             this.updateTitle(p.getTitle());
49369             this.tabs = null;
49370             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49371             this.setActivePanel(p);
49372         }
49373         panel.setRegion(null);
49374         if(this.activePanel == panel){
49375             this.activePanel = null;
49376         }
49377         if(this.config.autoDestroy !== false && preservePanel !== true){
49378             try{panel.destroy();}catch(e){}
49379         }
49380         this.fireEvent("panelremoved", this, panel);
49381         return panel;
49382     },
49383
49384     /**
49385      * Returns the TabPanel component used by this region
49386      * @return {Roo.TabPanel}
49387      */
49388     getTabs : function(){
49389         return this.tabs;
49390     },
49391
49392     createTool : function(parentEl, className){
49393         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49394             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49395         btn.addClassOnOver("x-layout-tools-button-over");
49396         return btn;
49397     }
49398 });/*
49399  * Based on:
49400  * Ext JS Library 1.1.1
49401  * Copyright(c) 2006-2007, Ext JS, LLC.
49402  *
49403  * Originally Released Under LGPL - original licence link has changed is not relivant.
49404  *
49405  * Fork - LGPL
49406  * <script type="text/javascript">
49407  */
49408  
49409
49410
49411 /**
49412  * @class Roo.SplitLayoutRegion
49413  * @extends Roo.LayoutRegion
49414  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49415  */
49416 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49417     this.cursor = cursor;
49418     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49419 };
49420
49421 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49422     splitTip : "Drag to resize.",
49423     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49424     useSplitTips : false,
49425
49426     applyConfig : function(config){
49427         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49428         if(config.split){
49429             if(!this.split){
49430                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49431                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49432                 /** The SplitBar for this region 
49433                 * @type Roo.SplitBar */
49434                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49435                 this.split.on("moved", this.onSplitMove, this);
49436                 this.split.useShim = config.useShim === true;
49437                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49438                 if(this.useSplitTips){
49439                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49440                 }
49441                 if(config.collapsible){
49442                     this.split.el.on("dblclick", this.collapse,  this);
49443                 }
49444             }
49445             if(typeof config.minSize != "undefined"){
49446                 this.split.minSize = config.minSize;
49447             }
49448             if(typeof config.maxSize != "undefined"){
49449                 this.split.maxSize = config.maxSize;
49450             }
49451             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49452                 this.hideSplitter();
49453             }
49454         }
49455     },
49456
49457     getHMaxSize : function(){
49458          var cmax = this.config.maxSize || 10000;
49459          var center = this.mgr.getRegion("center");
49460          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49461     },
49462
49463     getVMaxSize : function(){
49464          var cmax = this.config.maxSize || 10000;
49465          var center = this.mgr.getRegion("center");
49466          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49467     },
49468
49469     onSplitMove : function(split, newSize){
49470         this.fireEvent("resized", this, newSize);
49471     },
49472     
49473     /** 
49474      * Returns the {@link Roo.SplitBar} for this region.
49475      * @return {Roo.SplitBar}
49476      */
49477     getSplitBar : function(){
49478         return this.split;
49479     },
49480     
49481     hide : function(){
49482         this.hideSplitter();
49483         Roo.SplitLayoutRegion.superclass.hide.call(this);
49484     },
49485
49486     hideSplitter : function(){
49487         if(this.split){
49488             this.split.el.setLocation(-2000,-2000);
49489             this.split.el.hide();
49490         }
49491     },
49492
49493     show : function(){
49494         if(this.split){
49495             this.split.el.show();
49496         }
49497         Roo.SplitLayoutRegion.superclass.show.call(this);
49498     },
49499     
49500     beforeSlide: function(){
49501         if(Roo.isGecko){// firefox overflow auto bug workaround
49502             this.bodyEl.clip();
49503             if(this.tabs) this.tabs.bodyEl.clip();
49504             if(this.activePanel){
49505                 this.activePanel.getEl().clip();
49506                 
49507                 if(this.activePanel.beforeSlide){
49508                     this.activePanel.beforeSlide();
49509                 }
49510             }
49511         }
49512     },
49513     
49514     afterSlide : function(){
49515         if(Roo.isGecko){// firefox overflow auto bug workaround
49516             this.bodyEl.unclip();
49517             if(this.tabs) this.tabs.bodyEl.unclip();
49518             if(this.activePanel){
49519                 this.activePanel.getEl().unclip();
49520                 if(this.activePanel.afterSlide){
49521                     this.activePanel.afterSlide();
49522                 }
49523             }
49524         }
49525     },
49526
49527     initAutoHide : function(){
49528         if(this.autoHide !== false){
49529             if(!this.autoHideHd){
49530                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49531                 this.autoHideHd = {
49532                     "mouseout": function(e){
49533                         if(!e.within(this.el, true)){
49534                             st.delay(500);
49535                         }
49536                     },
49537                     "mouseover" : function(e){
49538                         st.cancel();
49539                     },
49540                     scope : this
49541                 };
49542             }
49543             this.el.on(this.autoHideHd);
49544         }
49545     },
49546
49547     clearAutoHide : function(){
49548         if(this.autoHide !== false){
49549             this.el.un("mouseout", this.autoHideHd.mouseout);
49550             this.el.un("mouseover", this.autoHideHd.mouseover);
49551         }
49552     },
49553
49554     clearMonitor : function(){
49555         Roo.get(document).un("click", this.slideInIf, this);
49556     },
49557
49558     // these names are backwards but not changed for compat
49559     slideOut : function(){
49560         if(this.isSlid || this.el.hasActiveFx()){
49561             return;
49562         }
49563         this.isSlid = true;
49564         if(this.collapseBtn){
49565             this.collapseBtn.hide();
49566         }
49567         this.closeBtnState = this.closeBtn.getStyle('display');
49568         this.closeBtn.hide();
49569         if(this.stickBtn){
49570             this.stickBtn.show();
49571         }
49572         this.el.show();
49573         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49574         this.beforeSlide();
49575         this.el.setStyle("z-index", 10001);
49576         this.el.slideIn(this.getSlideAnchor(), {
49577             callback: function(){
49578                 this.afterSlide();
49579                 this.initAutoHide();
49580                 Roo.get(document).on("click", this.slideInIf, this);
49581                 this.fireEvent("slideshow", this);
49582             },
49583             scope: this,
49584             block: true
49585         });
49586     },
49587
49588     afterSlideIn : function(){
49589         this.clearAutoHide();
49590         this.isSlid = false;
49591         this.clearMonitor();
49592         this.el.setStyle("z-index", "");
49593         if(this.collapseBtn){
49594             this.collapseBtn.show();
49595         }
49596         this.closeBtn.setStyle('display', this.closeBtnState);
49597         if(this.stickBtn){
49598             this.stickBtn.hide();
49599         }
49600         this.fireEvent("slidehide", this);
49601     },
49602
49603     slideIn : function(cb){
49604         if(!this.isSlid || this.el.hasActiveFx()){
49605             Roo.callback(cb);
49606             return;
49607         }
49608         this.isSlid = false;
49609         this.beforeSlide();
49610         this.el.slideOut(this.getSlideAnchor(), {
49611             callback: function(){
49612                 this.el.setLeftTop(-10000, -10000);
49613                 this.afterSlide();
49614                 this.afterSlideIn();
49615                 Roo.callback(cb);
49616             },
49617             scope: this,
49618             block: true
49619         });
49620     },
49621     
49622     slideInIf : function(e){
49623         if(!e.within(this.el)){
49624             this.slideIn();
49625         }
49626     },
49627
49628     animateCollapse : function(){
49629         this.beforeSlide();
49630         this.el.setStyle("z-index", 20000);
49631         var anchor = this.getSlideAnchor();
49632         this.el.slideOut(anchor, {
49633             callback : function(){
49634                 this.el.setStyle("z-index", "");
49635                 this.collapsedEl.slideIn(anchor, {duration:.3});
49636                 this.afterSlide();
49637                 this.el.setLocation(-10000,-10000);
49638                 this.el.hide();
49639                 this.fireEvent("collapsed", this);
49640             },
49641             scope: this,
49642             block: true
49643         });
49644     },
49645
49646     animateExpand : function(){
49647         this.beforeSlide();
49648         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49649         this.el.setStyle("z-index", 20000);
49650         this.collapsedEl.hide({
49651             duration:.1
49652         });
49653         this.el.slideIn(this.getSlideAnchor(), {
49654             callback : function(){
49655                 this.el.setStyle("z-index", "");
49656                 this.afterSlide();
49657                 if(this.split){
49658                     this.split.el.show();
49659                 }
49660                 this.fireEvent("invalidated", this);
49661                 this.fireEvent("expanded", this);
49662             },
49663             scope: this,
49664             block: true
49665         });
49666     },
49667
49668     anchors : {
49669         "west" : "left",
49670         "east" : "right",
49671         "north" : "top",
49672         "south" : "bottom"
49673     },
49674
49675     sanchors : {
49676         "west" : "l",
49677         "east" : "r",
49678         "north" : "t",
49679         "south" : "b"
49680     },
49681
49682     canchors : {
49683         "west" : "tl-tr",
49684         "east" : "tr-tl",
49685         "north" : "tl-bl",
49686         "south" : "bl-tl"
49687     },
49688
49689     getAnchor : function(){
49690         return this.anchors[this.position];
49691     },
49692
49693     getCollapseAnchor : function(){
49694         return this.canchors[this.position];
49695     },
49696
49697     getSlideAnchor : function(){
49698         return this.sanchors[this.position];
49699     },
49700
49701     getAlignAdj : function(){
49702         var cm = this.cmargins;
49703         switch(this.position){
49704             case "west":
49705                 return [0, 0];
49706             break;
49707             case "east":
49708                 return [0, 0];
49709             break;
49710             case "north":
49711                 return [0, 0];
49712             break;
49713             case "south":
49714                 return [0, 0];
49715             break;
49716         }
49717     },
49718
49719     getExpandAdj : function(){
49720         var c = this.collapsedEl, cm = this.cmargins;
49721         switch(this.position){
49722             case "west":
49723                 return [-(cm.right+c.getWidth()+cm.left), 0];
49724             break;
49725             case "east":
49726                 return [cm.right+c.getWidth()+cm.left, 0];
49727             break;
49728             case "north":
49729                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49730             break;
49731             case "south":
49732                 return [0, cm.top+cm.bottom+c.getHeight()];
49733             break;
49734         }
49735     }
49736 });/*
49737  * Based on:
49738  * Ext JS Library 1.1.1
49739  * Copyright(c) 2006-2007, Ext JS, LLC.
49740  *
49741  * Originally Released Under LGPL - original licence link has changed is not relivant.
49742  *
49743  * Fork - LGPL
49744  * <script type="text/javascript">
49745  */
49746 /*
49747  * These classes are private internal classes
49748  */
49749 Roo.CenterLayoutRegion = function(mgr, config){
49750     Roo.LayoutRegion.call(this, mgr, config, "center");
49751     this.visible = true;
49752     this.minWidth = config.minWidth || 20;
49753     this.minHeight = config.minHeight || 20;
49754 };
49755
49756 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49757     hide : function(){
49758         // center panel can't be hidden
49759     },
49760     
49761     show : function(){
49762         // center panel can't be hidden
49763     },
49764     
49765     getMinWidth: function(){
49766         return this.minWidth;
49767     },
49768     
49769     getMinHeight: function(){
49770         return this.minHeight;
49771     }
49772 });
49773
49774
49775 Roo.NorthLayoutRegion = function(mgr, config){
49776     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49777     if(this.split){
49778         this.split.placement = Roo.SplitBar.TOP;
49779         this.split.orientation = Roo.SplitBar.VERTICAL;
49780         this.split.el.addClass("x-layout-split-v");
49781     }
49782     var size = config.initialSize || config.height;
49783     if(typeof size != "undefined"){
49784         this.el.setHeight(size);
49785     }
49786 };
49787 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49788     orientation: Roo.SplitBar.VERTICAL,
49789     getBox : function(){
49790         if(this.collapsed){
49791             return this.collapsedEl.getBox();
49792         }
49793         var box = this.el.getBox();
49794         if(this.split){
49795             box.height += this.split.el.getHeight();
49796         }
49797         return box;
49798     },
49799     
49800     updateBox : function(box){
49801         if(this.split && !this.collapsed){
49802             box.height -= this.split.el.getHeight();
49803             this.split.el.setLeft(box.x);
49804             this.split.el.setTop(box.y+box.height);
49805             this.split.el.setWidth(box.width);
49806         }
49807         if(this.collapsed){
49808             this.updateBody(box.width, null);
49809         }
49810         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49811     }
49812 });
49813
49814 Roo.SouthLayoutRegion = function(mgr, config){
49815     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49816     if(this.split){
49817         this.split.placement = Roo.SplitBar.BOTTOM;
49818         this.split.orientation = Roo.SplitBar.VERTICAL;
49819         this.split.el.addClass("x-layout-split-v");
49820     }
49821     var size = config.initialSize || config.height;
49822     if(typeof size != "undefined"){
49823         this.el.setHeight(size);
49824     }
49825 };
49826 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49827     orientation: Roo.SplitBar.VERTICAL,
49828     getBox : function(){
49829         if(this.collapsed){
49830             return this.collapsedEl.getBox();
49831         }
49832         var box = this.el.getBox();
49833         if(this.split){
49834             var sh = this.split.el.getHeight();
49835             box.height += sh;
49836             box.y -= sh;
49837         }
49838         return box;
49839     },
49840     
49841     updateBox : function(box){
49842         if(this.split && !this.collapsed){
49843             var sh = this.split.el.getHeight();
49844             box.height -= sh;
49845             box.y += sh;
49846             this.split.el.setLeft(box.x);
49847             this.split.el.setTop(box.y-sh);
49848             this.split.el.setWidth(box.width);
49849         }
49850         if(this.collapsed){
49851             this.updateBody(box.width, null);
49852         }
49853         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49854     }
49855 });
49856
49857 Roo.EastLayoutRegion = function(mgr, config){
49858     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49859     if(this.split){
49860         this.split.placement = Roo.SplitBar.RIGHT;
49861         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49862         this.split.el.addClass("x-layout-split-h");
49863     }
49864     var size = config.initialSize || config.width;
49865     if(typeof size != "undefined"){
49866         this.el.setWidth(size);
49867     }
49868 };
49869 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49870     orientation: Roo.SplitBar.HORIZONTAL,
49871     getBox : function(){
49872         if(this.collapsed){
49873             return this.collapsedEl.getBox();
49874         }
49875         var box = this.el.getBox();
49876         if(this.split){
49877             var sw = this.split.el.getWidth();
49878             box.width += sw;
49879             box.x -= sw;
49880         }
49881         return box;
49882     },
49883
49884     updateBox : function(box){
49885         if(this.split && !this.collapsed){
49886             var sw = this.split.el.getWidth();
49887             box.width -= sw;
49888             this.split.el.setLeft(box.x);
49889             this.split.el.setTop(box.y);
49890             this.split.el.setHeight(box.height);
49891             box.x += sw;
49892         }
49893         if(this.collapsed){
49894             this.updateBody(null, box.height);
49895         }
49896         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49897     }
49898 });
49899
49900 Roo.WestLayoutRegion = function(mgr, config){
49901     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49902     if(this.split){
49903         this.split.placement = Roo.SplitBar.LEFT;
49904         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49905         this.split.el.addClass("x-layout-split-h");
49906     }
49907     var size = config.initialSize || config.width;
49908     if(typeof size != "undefined"){
49909         this.el.setWidth(size);
49910     }
49911 };
49912 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
49913     orientation: Roo.SplitBar.HORIZONTAL,
49914     getBox : function(){
49915         if(this.collapsed){
49916             return this.collapsedEl.getBox();
49917         }
49918         var box = this.el.getBox();
49919         if(this.split){
49920             box.width += this.split.el.getWidth();
49921         }
49922         return box;
49923     },
49924     
49925     updateBox : function(box){
49926         if(this.split && !this.collapsed){
49927             var sw = this.split.el.getWidth();
49928             box.width -= sw;
49929             this.split.el.setLeft(box.x+box.width);
49930             this.split.el.setTop(box.y);
49931             this.split.el.setHeight(box.height);
49932         }
49933         if(this.collapsed){
49934             this.updateBody(null, box.height);
49935         }
49936         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49937     }
49938 });
49939 /*
49940  * Based on:
49941  * Ext JS Library 1.1.1
49942  * Copyright(c) 2006-2007, Ext JS, LLC.
49943  *
49944  * Originally Released Under LGPL - original licence link has changed is not relivant.
49945  *
49946  * Fork - LGPL
49947  * <script type="text/javascript">
49948  */
49949  
49950  
49951 /*
49952  * Private internal class for reading and applying state
49953  */
49954 Roo.LayoutStateManager = function(layout){
49955      // default empty state
49956      this.state = {
49957         north: {},
49958         south: {},
49959         east: {},
49960         west: {}       
49961     };
49962 };
49963
49964 Roo.LayoutStateManager.prototype = {
49965     init : function(layout, provider){
49966         this.provider = provider;
49967         var state = provider.get(layout.id+"-layout-state");
49968         if(state){
49969             var wasUpdating = layout.isUpdating();
49970             if(!wasUpdating){
49971                 layout.beginUpdate();
49972             }
49973             for(var key in state){
49974                 if(typeof state[key] != "function"){
49975                     var rstate = state[key];
49976                     var r = layout.getRegion(key);
49977                     if(r && rstate){
49978                         if(rstate.size){
49979                             r.resizeTo(rstate.size);
49980                         }
49981                         if(rstate.collapsed == true){
49982                             r.collapse(true);
49983                         }else{
49984                             r.expand(null, true);
49985                         }
49986                     }
49987                 }
49988             }
49989             if(!wasUpdating){
49990                 layout.endUpdate();
49991             }
49992             this.state = state; 
49993         }
49994         this.layout = layout;
49995         layout.on("regionresized", this.onRegionResized, this);
49996         layout.on("regioncollapsed", this.onRegionCollapsed, this);
49997         layout.on("regionexpanded", this.onRegionExpanded, this);
49998     },
49999     
50000     storeState : function(){
50001         this.provider.set(this.layout.id+"-layout-state", this.state);
50002     },
50003     
50004     onRegionResized : function(region, newSize){
50005         this.state[region.getPosition()].size = newSize;
50006         this.storeState();
50007     },
50008     
50009     onRegionCollapsed : function(region){
50010         this.state[region.getPosition()].collapsed = true;
50011         this.storeState();
50012     },
50013     
50014     onRegionExpanded : function(region){
50015         this.state[region.getPosition()].collapsed = false;
50016         this.storeState();
50017     }
50018 };/*
50019  * Based on:
50020  * Ext JS Library 1.1.1
50021  * Copyright(c) 2006-2007, Ext JS, LLC.
50022  *
50023  * Originally Released Under LGPL - original licence link has changed is not relivant.
50024  *
50025  * Fork - LGPL
50026  * <script type="text/javascript">
50027  */
50028 /**
50029  * @class Roo.ContentPanel
50030  * @extends Roo.util.Observable
50031  * A basic ContentPanel element.
50032  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50033  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50034  * @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
50035  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50036  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50037  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50038  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50039  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50040  * @cfg {String} title          The title for this panel
50041  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50042  * @cfg {String} url            Calls {@link #setUrl} with this value
50043  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50044  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50045  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50046  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50047
50048  * @constructor
50049  * Create a new ContentPanel.
50050  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50051  * @param {String/Object} config A string to set only the title or a config object
50052  * @param {String} content (optional) Set the HTML content for this panel
50053  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50054  */
50055 Roo.ContentPanel = function(el, config, content){
50056     
50057      
50058     /*
50059     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50060         config = el;
50061         el = Roo.id();
50062     }
50063     if (config && config.parentLayout) { 
50064         el = config.parentLayout.el.createChild(); 
50065     }
50066     */
50067     if(el.autoCreate){ // xtype is available if this is called from factory
50068         config = el;
50069         el = Roo.id();
50070     }
50071     this.el = Roo.get(el);
50072     if(!this.el && config && config.autoCreate){
50073         if(typeof config.autoCreate == "object"){
50074             if(!config.autoCreate.id){
50075                 config.autoCreate.id = config.id||el;
50076             }
50077             this.el = Roo.DomHelper.append(document.body,
50078                         config.autoCreate, true);
50079         }else{
50080             this.el = Roo.DomHelper.append(document.body,
50081                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50082         }
50083     }
50084     this.closable = false;
50085     this.loaded = false;
50086     this.active = false;
50087     if(typeof config == "string"){
50088         this.title = config;
50089     }else{
50090         Roo.apply(this, config);
50091     }
50092     
50093     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50094         this.wrapEl = this.el.wrap();
50095         this.toolbar.container = this.el.insertSibling(false, 'before');
50096         this.toolbar = new Roo.Toolbar(this.toolbar);
50097     }
50098     
50099     // xtype created footer. - not sure if will work as we normally have to render first..
50100     if (this.footer && !this.footer.el && this.footer.xtype) {
50101         if (!this.wrapEl) {
50102             this.wrapEl = this.el.wrap();
50103         }
50104     
50105         this.footer.container = this.wrapEl.createChild();
50106          
50107         this.footer = Roo.factory(this.footer, Roo);
50108         
50109     }
50110     
50111     if(this.resizeEl){
50112         this.resizeEl = Roo.get(this.resizeEl, true);
50113     }else{
50114         this.resizeEl = this.el;
50115     }
50116     // handle view.xtype
50117     
50118  
50119     
50120     
50121     this.addEvents({
50122         /**
50123          * @event activate
50124          * Fires when this panel is activated. 
50125          * @param {Roo.ContentPanel} this
50126          */
50127         "activate" : true,
50128         /**
50129          * @event deactivate
50130          * Fires when this panel is activated. 
50131          * @param {Roo.ContentPanel} this
50132          */
50133         "deactivate" : true,
50134
50135         /**
50136          * @event resize
50137          * Fires when this panel is resized if fitToFrame is true.
50138          * @param {Roo.ContentPanel} this
50139          * @param {Number} width The width after any component adjustments
50140          * @param {Number} height The height after any component adjustments
50141          */
50142         "resize" : true,
50143         
50144          /**
50145          * @event render
50146          * Fires when this tab is created
50147          * @param {Roo.ContentPanel} this
50148          */
50149         "render" : true
50150         
50151         
50152         
50153     });
50154     
50155
50156     
50157     
50158     if(this.autoScroll){
50159         this.resizeEl.setStyle("overflow", "auto");
50160     } else {
50161         // fix randome scrolling
50162         this.el.on('scroll', function() {
50163             Roo.log('fix random scolling');
50164             this.scrollTo('top',0); 
50165         });
50166     }
50167     content = content || this.content;
50168     if(content){
50169         this.setContent(content);
50170     }
50171     if(config && config.url){
50172         this.setUrl(this.url, this.params, this.loadOnce);
50173     }
50174     
50175     
50176     
50177     Roo.ContentPanel.superclass.constructor.call(this);
50178     
50179     if (this.view && typeof(this.view.xtype) != 'undefined') {
50180         this.view.el = this.el.appendChild(document.createElement("div"));
50181         this.view = Roo.factory(this.view); 
50182         this.view.render  &&  this.view.render(false, '');  
50183     }
50184     
50185     
50186     this.fireEvent('render', this);
50187 };
50188
50189 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50190     tabTip:'',
50191     setRegion : function(region){
50192         this.region = region;
50193         if(region){
50194            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50195         }else{
50196            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50197         } 
50198     },
50199     
50200     /**
50201      * Returns the toolbar for this Panel if one was configured. 
50202      * @return {Roo.Toolbar} 
50203      */
50204     getToolbar : function(){
50205         return this.toolbar;
50206     },
50207     
50208     setActiveState : function(active){
50209         this.active = active;
50210         if(!active){
50211             this.fireEvent("deactivate", this);
50212         }else{
50213             this.fireEvent("activate", this);
50214         }
50215     },
50216     /**
50217      * Updates this panel's element
50218      * @param {String} content The new content
50219      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50220     */
50221     setContent : function(content, loadScripts){
50222         this.el.update(content, loadScripts);
50223     },
50224
50225     ignoreResize : function(w, h){
50226         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50227             return true;
50228         }else{
50229             this.lastSize = {width: w, height: h};
50230             return false;
50231         }
50232     },
50233     /**
50234      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50235      * @return {Roo.UpdateManager} The UpdateManager
50236      */
50237     getUpdateManager : function(){
50238         return this.el.getUpdateManager();
50239     },
50240      /**
50241      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50242      * @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:
50243 <pre><code>
50244 panel.load({
50245     url: "your-url.php",
50246     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50247     callback: yourFunction,
50248     scope: yourObject, //(optional scope)
50249     discardUrl: false,
50250     nocache: false,
50251     text: "Loading...",
50252     timeout: 30,
50253     scripts: false
50254 });
50255 </code></pre>
50256      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50257      * 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.
50258      * @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}
50259      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50260      * @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.
50261      * @return {Roo.ContentPanel} this
50262      */
50263     load : function(){
50264         var um = this.el.getUpdateManager();
50265         um.update.apply(um, arguments);
50266         return this;
50267     },
50268
50269
50270     /**
50271      * 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.
50272      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50273      * @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)
50274      * @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)
50275      * @return {Roo.UpdateManager} The UpdateManager
50276      */
50277     setUrl : function(url, params, loadOnce){
50278         if(this.refreshDelegate){
50279             this.removeListener("activate", this.refreshDelegate);
50280         }
50281         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50282         this.on("activate", this.refreshDelegate);
50283         return this.el.getUpdateManager();
50284     },
50285     
50286     _handleRefresh : function(url, params, loadOnce){
50287         if(!loadOnce || !this.loaded){
50288             var updater = this.el.getUpdateManager();
50289             updater.update(url, params, this._setLoaded.createDelegate(this));
50290         }
50291     },
50292     
50293     _setLoaded : function(){
50294         this.loaded = true;
50295     }, 
50296     
50297     /**
50298      * Returns this panel's id
50299      * @return {String} 
50300      */
50301     getId : function(){
50302         return this.el.id;
50303     },
50304     
50305     /** 
50306      * Returns this panel's element - used by regiosn to add.
50307      * @return {Roo.Element} 
50308      */
50309     getEl : function(){
50310         return this.wrapEl || this.el;
50311     },
50312     
50313     adjustForComponents : function(width, height)
50314     {
50315         //Roo.log('adjustForComponents ');
50316         if(this.resizeEl != this.el){
50317             width -= this.el.getFrameWidth('lr');
50318             height -= this.el.getFrameWidth('tb');
50319         }
50320         if(this.toolbar){
50321             var te = this.toolbar.getEl();
50322             height -= te.getHeight();
50323             te.setWidth(width);
50324         }
50325         if(this.footer){
50326             var te = this.footer.getEl();
50327             Roo.log("footer:" + te.getHeight());
50328             
50329             height -= te.getHeight();
50330             te.setWidth(width);
50331         }
50332         
50333         
50334         if(this.adjustments){
50335             width += this.adjustments[0];
50336             height += this.adjustments[1];
50337         }
50338         return {"width": width, "height": height};
50339     },
50340     
50341     setSize : function(width, height){
50342         if(this.fitToFrame && !this.ignoreResize(width, height)){
50343             if(this.fitContainer && this.resizeEl != this.el){
50344                 this.el.setSize(width, height);
50345             }
50346             var size = this.adjustForComponents(width, height);
50347             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50348             this.fireEvent('resize', this, size.width, size.height);
50349         }
50350     },
50351     
50352     /**
50353      * Returns this panel's title
50354      * @return {String} 
50355      */
50356     getTitle : function(){
50357         return this.title;
50358     },
50359     
50360     /**
50361      * Set this panel's title
50362      * @param {String} title
50363      */
50364     setTitle : function(title){
50365         this.title = title;
50366         if(this.region){
50367             this.region.updatePanelTitle(this, title);
50368         }
50369     },
50370     
50371     /**
50372      * Returns true is this panel was configured to be closable
50373      * @return {Boolean} 
50374      */
50375     isClosable : function(){
50376         return this.closable;
50377     },
50378     
50379     beforeSlide : function(){
50380         this.el.clip();
50381         this.resizeEl.clip();
50382     },
50383     
50384     afterSlide : function(){
50385         this.el.unclip();
50386         this.resizeEl.unclip();
50387     },
50388     
50389     /**
50390      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50391      *   Will fail silently if the {@link #setUrl} method has not been called.
50392      *   This does not activate the panel, just updates its content.
50393      */
50394     refresh : function(){
50395         if(this.refreshDelegate){
50396            this.loaded = false;
50397            this.refreshDelegate();
50398         }
50399     },
50400     
50401     /**
50402      * Destroys this panel
50403      */
50404     destroy : function(){
50405         this.el.removeAllListeners();
50406         var tempEl = document.createElement("span");
50407         tempEl.appendChild(this.el.dom);
50408         tempEl.innerHTML = "";
50409         this.el.remove();
50410         this.el = null;
50411     },
50412     
50413     /**
50414      * form - if the content panel contains a form - this is a reference to it.
50415      * @type {Roo.form.Form}
50416      */
50417     form : false,
50418     /**
50419      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50420      *    This contains a reference to it.
50421      * @type {Roo.View}
50422      */
50423     view : false,
50424     
50425       /**
50426      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50427      * <pre><code>
50428
50429 layout.addxtype({
50430        xtype : 'Form',
50431        items: [ .... ]
50432    }
50433 );
50434
50435 </code></pre>
50436      * @param {Object} cfg Xtype definition of item to add.
50437      */
50438     
50439     addxtype : function(cfg) {
50440         // add form..
50441         if (cfg.xtype.match(/^Form$/)) {
50442             
50443             var el;
50444             //if (this.footer) {
50445             //    el = this.footer.container.insertSibling(false, 'before');
50446             //} else {
50447                 el = this.el.createChild();
50448             //}
50449
50450             this.form = new  Roo.form.Form(cfg);
50451             
50452             
50453             if ( this.form.allItems.length) this.form.render(el.dom);
50454             return this.form;
50455         }
50456         // should only have one of theses..
50457         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50458             // views.. should not be just added - used named prop 'view''
50459             
50460             cfg.el = this.el.appendChild(document.createElement("div"));
50461             // factory?
50462             
50463             var ret = new Roo.factory(cfg);
50464              
50465              ret.render && ret.render(false, ''); // render blank..
50466             this.view = ret;
50467             return ret;
50468         }
50469         return false;
50470     }
50471 });
50472
50473 /**
50474  * @class Roo.GridPanel
50475  * @extends Roo.ContentPanel
50476  * @constructor
50477  * Create a new GridPanel.
50478  * @param {Roo.grid.Grid} grid The grid for this panel
50479  * @param {String/Object} config A string to set only the panel's title, or a config object
50480  */
50481 Roo.GridPanel = function(grid, config){
50482     
50483   
50484     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50485         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50486         
50487     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50488     
50489     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50490     
50491     if(this.toolbar){
50492         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50493     }
50494     // xtype created footer. - not sure if will work as we normally have to render first..
50495     if (this.footer && !this.footer.el && this.footer.xtype) {
50496         
50497         this.footer.container = this.grid.getView().getFooterPanel(true);
50498         this.footer.dataSource = this.grid.dataSource;
50499         this.footer = Roo.factory(this.footer, Roo);
50500         
50501     }
50502     
50503     grid.monitorWindowResize = false; // turn off autosizing
50504     grid.autoHeight = false;
50505     grid.autoWidth = false;
50506     this.grid = grid;
50507     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50508 };
50509
50510 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50511     getId : function(){
50512         return this.grid.id;
50513     },
50514     
50515     /**
50516      * Returns the grid for this panel
50517      * @return {Roo.grid.Grid} 
50518      */
50519     getGrid : function(){
50520         return this.grid;    
50521     },
50522     
50523     setSize : function(width, height){
50524         if(!this.ignoreResize(width, height)){
50525             var grid = this.grid;
50526             var size = this.adjustForComponents(width, height);
50527             grid.getGridEl().setSize(size.width, size.height);
50528             grid.autoSize();
50529         }
50530     },
50531     
50532     beforeSlide : function(){
50533         this.grid.getView().scroller.clip();
50534     },
50535     
50536     afterSlide : function(){
50537         this.grid.getView().scroller.unclip();
50538     },
50539     
50540     destroy : function(){
50541         this.grid.destroy();
50542         delete this.grid;
50543         Roo.GridPanel.superclass.destroy.call(this); 
50544     }
50545 });
50546
50547
50548 /**
50549  * @class Roo.NestedLayoutPanel
50550  * @extends Roo.ContentPanel
50551  * @constructor
50552  * Create a new NestedLayoutPanel.
50553  * 
50554  * 
50555  * @param {Roo.BorderLayout} layout The layout for this panel
50556  * @param {String/Object} config A string to set only the title or a config object
50557  */
50558 Roo.NestedLayoutPanel = function(layout, config)
50559 {
50560     // construct with only one argument..
50561     /* FIXME - implement nicer consturctors
50562     if (layout.layout) {
50563         config = layout;
50564         layout = config.layout;
50565         delete config.layout;
50566     }
50567     if (layout.xtype && !layout.getEl) {
50568         // then layout needs constructing..
50569         layout = Roo.factory(layout, Roo);
50570     }
50571     */
50572     
50573     
50574     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50575     
50576     layout.monitorWindowResize = false; // turn off autosizing
50577     this.layout = layout;
50578     this.layout.getEl().addClass("x-layout-nested-layout");
50579     
50580     
50581     
50582     
50583 };
50584
50585 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50586
50587     setSize : function(width, height){
50588         if(!this.ignoreResize(width, height)){
50589             var size = this.adjustForComponents(width, height);
50590             var el = this.layout.getEl();
50591             el.setSize(size.width, size.height);
50592             var touch = el.dom.offsetWidth;
50593             this.layout.layout();
50594             // ie requires a double layout on the first pass
50595             if(Roo.isIE && !this.initialized){
50596                 this.initialized = true;
50597                 this.layout.layout();
50598             }
50599         }
50600     },
50601     
50602     // activate all subpanels if not currently active..
50603     
50604     setActiveState : function(active){
50605         this.active = active;
50606         if(!active){
50607             this.fireEvent("deactivate", this);
50608             return;
50609         }
50610         
50611         this.fireEvent("activate", this);
50612         // not sure if this should happen before or after..
50613         if (!this.layout) {
50614             return; // should not happen..
50615         }
50616         var reg = false;
50617         for (var r in this.layout.regions) {
50618             reg = this.layout.getRegion(r);
50619             if (reg.getActivePanel()) {
50620                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50621                 reg.setActivePanel(reg.getActivePanel());
50622                 continue;
50623             }
50624             if (!reg.panels.length) {
50625                 continue;
50626             }
50627             reg.showPanel(reg.getPanel(0));
50628         }
50629         
50630         
50631         
50632         
50633     },
50634     
50635     /**
50636      * Returns the nested BorderLayout for this panel
50637      * @return {Roo.BorderLayout} 
50638      */
50639     getLayout : function(){
50640         return this.layout;
50641     },
50642     
50643      /**
50644      * Adds a xtype elements to the layout of the nested panel
50645      * <pre><code>
50646
50647 panel.addxtype({
50648        xtype : 'ContentPanel',
50649        region: 'west',
50650        items: [ .... ]
50651    }
50652 );
50653
50654 panel.addxtype({
50655         xtype : 'NestedLayoutPanel',
50656         region: 'west',
50657         layout: {
50658            center: { },
50659            west: { }   
50660         },
50661         items : [ ... list of content panels or nested layout panels.. ]
50662    }
50663 );
50664 </code></pre>
50665      * @param {Object} cfg Xtype definition of item to add.
50666      */
50667     addxtype : function(cfg) {
50668         return this.layout.addxtype(cfg);
50669     
50670     }
50671 });
50672
50673 Roo.ScrollPanel = function(el, config, content){
50674     config = config || {};
50675     config.fitToFrame = true;
50676     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50677     
50678     this.el.dom.style.overflow = "hidden";
50679     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50680     this.el.removeClass("x-layout-inactive-content");
50681     this.el.on("mousewheel", this.onWheel, this);
50682
50683     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50684     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50685     up.unselectable(); down.unselectable();
50686     up.on("click", this.scrollUp, this);
50687     down.on("click", this.scrollDown, this);
50688     up.addClassOnOver("x-scroller-btn-over");
50689     down.addClassOnOver("x-scroller-btn-over");
50690     up.addClassOnClick("x-scroller-btn-click");
50691     down.addClassOnClick("x-scroller-btn-click");
50692     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50693
50694     this.resizeEl = this.el;
50695     this.el = wrap; this.up = up; this.down = down;
50696 };
50697
50698 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50699     increment : 100,
50700     wheelIncrement : 5,
50701     scrollUp : function(){
50702         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50703     },
50704
50705     scrollDown : function(){
50706         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50707     },
50708
50709     afterScroll : function(){
50710         var el = this.resizeEl;
50711         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50712         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50713         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50714     },
50715
50716     setSize : function(){
50717         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50718         this.afterScroll();
50719     },
50720
50721     onWheel : function(e){
50722         var d = e.getWheelDelta();
50723         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50724         this.afterScroll();
50725         e.stopEvent();
50726     },
50727
50728     setContent : function(content, loadScripts){
50729         this.resizeEl.update(content, loadScripts);
50730     }
50731
50732 });
50733
50734
50735
50736
50737
50738
50739
50740
50741
50742 /**
50743  * @class Roo.TreePanel
50744  * @extends Roo.ContentPanel
50745  * @constructor
50746  * Create a new TreePanel. - defaults to fit/scoll contents.
50747  * @param {String/Object} config A string to set only the panel's title, or a config object
50748  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50749  */
50750 Roo.TreePanel = function(config){
50751     var el = config.el;
50752     var tree = config.tree;
50753     delete config.tree; 
50754     delete config.el; // hopefull!
50755     
50756     // wrapper for IE7 strict & safari scroll issue
50757     
50758     var treeEl = el.createChild();
50759     config.resizeEl = treeEl;
50760     
50761     
50762     
50763     Roo.TreePanel.superclass.constructor.call(this, el, config);
50764  
50765  
50766     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50767     //console.log(tree);
50768     this.on('activate', function()
50769     {
50770         if (this.tree.rendered) {
50771             return;
50772         }
50773         //console.log('render tree');
50774         this.tree.render();
50775     });
50776     // this should not be needed.. - it's actually the 'el' that resizes?
50777     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50778     
50779     //this.on('resize',  function (cp, w, h) {
50780     //        this.tree.innerCt.setWidth(w);
50781     //        this.tree.innerCt.setHeight(h);
50782     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50783     //});
50784
50785         
50786     
50787 };
50788
50789 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50790     fitToFrame : true,
50791     autoScroll : true
50792 });
50793
50794
50795
50796
50797
50798
50799
50800
50801
50802
50803
50804 /*
50805  * Based on:
50806  * Ext JS Library 1.1.1
50807  * Copyright(c) 2006-2007, Ext JS, LLC.
50808  *
50809  * Originally Released Under LGPL - original licence link has changed is not relivant.
50810  *
50811  * Fork - LGPL
50812  * <script type="text/javascript">
50813  */
50814  
50815
50816 /**
50817  * @class Roo.ReaderLayout
50818  * @extends Roo.BorderLayout
50819  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50820  * center region containing two nested regions (a top one for a list view and one for item preview below),
50821  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50822  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50823  * expedites the setup of the overall layout and regions for this common application style.
50824  * Example:
50825  <pre><code>
50826 var reader = new Roo.ReaderLayout();
50827 var CP = Roo.ContentPanel;  // shortcut for adding
50828
50829 reader.beginUpdate();
50830 reader.add("north", new CP("north", "North"));
50831 reader.add("west", new CP("west", {title: "West"}));
50832 reader.add("east", new CP("east", {title: "East"}));
50833
50834 reader.regions.listView.add(new CP("listView", "List"));
50835 reader.regions.preview.add(new CP("preview", "Preview"));
50836 reader.endUpdate();
50837 </code></pre>
50838 * @constructor
50839 * Create a new ReaderLayout
50840 * @param {Object} config Configuration options
50841 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50842 * document.body if omitted)
50843 */
50844 Roo.ReaderLayout = function(config, renderTo){
50845     var c = config || {size:{}};
50846     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50847         north: c.north !== false ? Roo.apply({
50848             split:false,
50849             initialSize: 32,
50850             titlebar: false
50851         }, c.north) : false,
50852         west: c.west !== false ? Roo.apply({
50853             split:true,
50854             initialSize: 200,
50855             minSize: 175,
50856             maxSize: 400,
50857             titlebar: true,
50858             collapsible: true,
50859             animate: true,
50860             margins:{left:5,right:0,bottom:5,top:5},
50861             cmargins:{left:5,right:5,bottom:5,top:5}
50862         }, c.west) : false,
50863         east: c.east !== false ? Roo.apply({
50864             split:true,
50865             initialSize: 200,
50866             minSize: 175,
50867             maxSize: 400,
50868             titlebar: true,
50869             collapsible: true,
50870             animate: true,
50871             margins:{left:0,right:5,bottom:5,top:5},
50872             cmargins:{left:5,right:5,bottom:5,top:5}
50873         }, c.east) : false,
50874         center: Roo.apply({
50875             tabPosition: 'top',
50876             autoScroll:false,
50877             closeOnTab: true,
50878             titlebar:false,
50879             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50880         }, c.center)
50881     });
50882
50883     this.el.addClass('x-reader');
50884
50885     this.beginUpdate();
50886
50887     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50888         south: c.preview !== false ? Roo.apply({
50889             split:true,
50890             initialSize: 200,
50891             minSize: 100,
50892             autoScroll:true,
50893             collapsible:true,
50894             titlebar: true,
50895             cmargins:{top:5,left:0, right:0, bottom:0}
50896         }, c.preview) : false,
50897         center: Roo.apply({
50898             autoScroll:false,
50899             titlebar:false,
50900             minHeight:200
50901         }, c.listView)
50902     });
50903     this.add('center', new Roo.NestedLayoutPanel(inner,
50904             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50905
50906     this.endUpdate();
50907
50908     this.regions.preview = inner.getRegion('south');
50909     this.regions.listView = inner.getRegion('center');
50910 };
50911
50912 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
50913  * Based on:
50914  * Ext JS Library 1.1.1
50915  * Copyright(c) 2006-2007, Ext JS, LLC.
50916  *
50917  * Originally Released Under LGPL - original licence link has changed is not relivant.
50918  *
50919  * Fork - LGPL
50920  * <script type="text/javascript">
50921  */
50922  
50923 /**
50924  * @class Roo.grid.Grid
50925  * @extends Roo.util.Observable
50926  * This class represents the primary interface of a component based grid control.
50927  * <br><br>Usage:<pre><code>
50928  var grid = new Roo.grid.Grid("my-container-id", {
50929      ds: myDataStore,
50930      cm: myColModel,
50931      selModel: mySelectionModel,
50932      autoSizeColumns: true,
50933      monitorWindowResize: false,
50934      trackMouseOver: true
50935  });
50936  // set any options
50937  grid.render();
50938  * </code></pre>
50939  * <b>Common Problems:</b><br/>
50940  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
50941  * element will correct this<br/>
50942  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
50943  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
50944  * are unpredictable.<br/>
50945  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
50946  * grid to calculate dimensions/offsets.<br/>
50947   * @constructor
50948  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50949  * The container MUST have some type of size defined for the grid to fill. The container will be
50950  * automatically set to position relative if it isn't already.
50951  * @param {Object} config A config object that sets properties on this grid.
50952  */
50953 Roo.grid.Grid = function(container, config){
50954         // initialize the container
50955         this.container = Roo.get(container);
50956         this.container.update("");
50957         this.container.setStyle("overflow", "hidden");
50958     this.container.addClass('x-grid-container');
50959
50960     this.id = this.container.id;
50961
50962     Roo.apply(this, config);
50963     // check and correct shorthanded configs
50964     if(this.ds){
50965         this.dataSource = this.ds;
50966         delete this.ds;
50967     }
50968     if(this.cm){
50969         this.colModel = this.cm;
50970         delete this.cm;
50971     }
50972     if(this.sm){
50973         this.selModel = this.sm;
50974         delete this.sm;
50975     }
50976
50977     if (this.selModel) {
50978         this.selModel = Roo.factory(this.selModel, Roo.grid);
50979         this.sm = this.selModel;
50980         this.sm.xmodule = this.xmodule || false;
50981     }
50982     if (typeof(this.colModel.config) == 'undefined') {
50983         this.colModel = new Roo.grid.ColumnModel(this.colModel);
50984         this.cm = this.colModel;
50985         this.cm.xmodule = this.xmodule || false;
50986     }
50987     if (this.dataSource) {
50988         this.dataSource= Roo.factory(this.dataSource, Roo.data);
50989         this.ds = this.dataSource;
50990         this.ds.xmodule = this.xmodule || false;
50991          
50992     }
50993     
50994     
50995     
50996     if(this.width){
50997         this.container.setWidth(this.width);
50998     }
50999
51000     if(this.height){
51001         this.container.setHeight(this.height);
51002     }
51003     /** @private */
51004         this.addEvents({
51005         // raw events
51006         /**
51007          * @event click
51008          * The raw click event for the entire grid.
51009          * @param {Roo.EventObject} e
51010          */
51011         "click" : true,
51012         /**
51013          * @event dblclick
51014          * The raw dblclick event for the entire grid.
51015          * @param {Roo.EventObject} e
51016          */
51017         "dblclick" : true,
51018         /**
51019          * @event contextmenu
51020          * The raw contextmenu event for the entire grid.
51021          * @param {Roo.EventObject} e
51022          */
51023         "contextmenu" : true,
51024         /**
51025          * @event mousedown
51026          * The raw mousedown event for the entire grid.
51027          * @param {Roo.EventObject} e
51028          */
51029         "mousedown" : true,
51030         /**
51031          * @event mouseup
51032          * The raw mouseup event for the entire grid.
51033          * @param {Roo.EventObject} e
51034          */
51035         "mouseup" : true,
51036         /**
51037          * @event mouseover
51038          * The raw mouseover event for the entire grid.
51039          * @param {Roo.EventObject} e
51040          */
51041         "mouseover" : true,
51042         /**
51043          * @event mouseout
51044          * The raw mouseout event for the entire grid.
51045          * @param {Roo.EventObject} e
51046          */
51047         "mouseout" : true,
51048         /**
51049          * @event keypress
51050          * The raw keypress event for the entire grid.
51051          * @param {Roo.EventObject} e
51052          */
51053         "keypress" : true,
51054         /**
51055          * @event keydown
51056          * The raw keydown event for the entire grid.
51057          * @param {Roo.EventObject} e
51058          */
51059         "keydown" : true,
51060
51061         // custom events
51062
51063         /**
51064          * @event cellclick
51065          * Fires when a cell is clicked
51066          * @param {Grid} this
51067          * @param {Number} rowIndex
51068          * @param {Number} columnIndex
51069          * @param {Roo.EventObject} e
51070          */
51071         "cellclick" : true,
51072         /**
51073          * @event celldblclick
51074          * Fires when a cell is double clicked
51075          * @param {Grid} this
51076          * @param {Number} rowIndex
51077          * @param {Number} columnIndex
51078          * @param {Roo.EventObject} e
51079          */
51080         "celldblclick" : true,
51081         /**
51082          * @event rowclick
51083          * Fires when a row is clicked
51084          * @param {Grid} this
51085          * @param {Number} rowIndex
51086          * @param {Roo.EventObject} e
51087          */
51088         "rowclick" : true,
51089         /**
51090          * @event rowdblclick
51091          * Fires when a row is double clicked
51092          * @param {Grid} this
51093          * @param {Number} rowIndex
51094          * @param {Roo.EventObject} e
51095          */
51096         "rowdblclick" : true,
51097         /**
51098          * @event headerclick
51099          * Fires when a header is clicked
51100          * @param {Grid} this
51101          * @param {Number} columnIndex
51102          * @param {Roo.EventObject} e
51103          */
51104         "headerclick" : true,
51105         /**
51106          * @event headerdblclick
51107          * Fires when a header cell is double clicked
51108          * @param {Grid} this
51109          * @param {Number} columnIndex
51110          * @param {Roo.EventObject} e
51111          */
51112         "headerdblclick" : true,
51113         /**
51114          * @event rowcontextmenu
51115          * Fires when a row is right clicked
51116          * @param {Grid} this
51117          * @param {Number} rowIndex
51118          * @param {Roo.EventObject} e
51119          */
51120         "rowcontextmenu" : true,
51121         /**
51122          * @event cellcontextmenu
51123          * Fires when a cell is right clicked
51124          * @param {Grid} this
51125          * @param {Number} rowIndex
51126          * @param {Number} cellIndex
51127          * @param {Roo.EventObject} e
51128          */
51129          "cellcontextmenu" : true,
51130         /**
51131          * @event headercontextmenu
51132          * Fires when a header is right clicked
51133          * @param {Grid} this
51134          * @param {Number} columnIndex
51135          * @param {Roo.EventObject} e
51136          */
51137         "headercontextmenu" : true,
51138         /**
51139          * @event bodyscroll
51140          * Fires when the body element is scrolled
51141          * @param {Number} scrollLeft
51142          * @param {Number} scrollTop
51143          */
51144         "bodyscroll" : true,
51145         /**
51146          * @event columnresize
51147          * Fires when the user resizes a column
51148          * @param {Number} columnIndex
51149          * @param {Number} newSize
51150          */
51151         "columnresize" : true,
51152         /**
51153          * @event columnmove
51154          * Fires when the user moves a column
51155          * @param {Number} oldIndex
51156          * @param {Number} newIndex
51157          */
51158         "columnmove" : true,
51159         /**
51160          * @event startdrag
51161          * Fires when row(s) start being dragged
51162          * @param {Grid} this
51163          * @param {Roo.GridDD} dd The drag drop object
51164          * @param {event} e The raw browser event
51165          */
51166         "startdrag" : true,
51167         /**
51168          * @event enddrag
51169          * Fires when a drag operation is complete
51170          * @param {Grid} this
51171          * @param {Roo.GridDD} dd The drag drop object
51172          * @param {event} e The raw browser event
51173          */
51174         "enddrag" : true,
51175         /**
51176          * @event dragdrop
51177          * Fires when dragged row(s) are dropped on a valid DD target
51178          * @param {Grid} this
51179          * @param {Roo.GridDD} dd The drag drop object
51180          * @param {String} targetId The target drag drop object
51181          * @param {event} e The raw browser event
51182          */
51183         "dragdrop" : true,
51184         /**
51185          * @event dragover
51186          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51187          * @param {Grid} this
51188          * @param {Roo.GridDD} dd The drag drop object
51189          * @param {String} targetId The target drag drop object
51190          * @param {event} e The raw browser event
51191          */
51192         "dragover" : true,
51193         /**
51194          * @event dragenter
51195          *  Fires when the dragged row(s) first cross another DD target while being dragged
51196          * @param {Grid} this
51197          * @param {Roo.GridDD} dd The drag drop object
51198          * @param {String} targetId The target drag drop object
51199          * @param {event} e The raw browser event
51200          */
51201         "dragenter" : true,
51202         /**
51203          * @event dragout
51204          * Fires when the dragged row(s) leave another DD target while being dragged
51205          * @param {Grid} this
51206          * @param {Roo.GridDD} dd The drag drop object
51207          * @param {String} targetId The target drag drop object
51208          * @param {event} e The raw browser event
51209          */
51210         "dragout" : true,
51211         /**
51212          * @event rowclass
51213          * Fires when a row is rendered, so you can change add a style to it.
51214          * @param {GridView} gridview   The grid view
51215          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51216          */
51217         'rowclass' : true,
51218
51219         /**
51220          * @event render
51221          * Fires when the grid is rendered
51222          * @param {Grid} grid
51223          */
51224         'render' : true
51225     });
51226
51227     Roo.grid.Grid.superclass.constructor.call(this);
51228 };
51229 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51230     
51231     /**
51232      * @cfg {String} ddGroup - drag drop group.
51233      */
51234
51235     /**
51236      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51237      */
51238     minColumnWidth : 25,
51239
51240     /**
51241      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51242      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51243      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51244      */
51245     autoSizeColumns : false,
51246
51247     /**
51248      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51249      */
51250     autoSizeHeaders : true,
51251
51252     /**
51253      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51254      */
51255     monitorWindowResize : true,
51256
51257     /**
51258      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51259      * rows measured to get a columns size. Default is 0 (all rows).
51260      */
51261     maxRowsToMeasure : 0,
51262
51263     /**
51264      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51265      */
51266     trackMouseOver : true,
51267
51268     /**
51269     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51270     */
51271     
51272     /**
51273     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51274     */
51275     enableDragDrop : false,
51276     
51277     /**
51278     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51279     */
51280     enableColumnMove : true,
51281     
51282     /**
51283     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51284     */
51285     enableColumnHide : true,
51286     
51287     /**
51288     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51289     */
51290     enableRowHeightSync : false,
51291     
51292     /**
51293     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51294     */
51295     stripeRows : true,
51296     
51297     /**
51298     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51299     */
51300     autoHeight : false,
51301
51302     /**
51303      * @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.
51304      */
51305     autoExpandColumn : false,
51306
51307     /**
51308     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51309     * Default is 50.
51310     */
51311     autoExpandMin : 50,
51312
51313     /**
51314     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51315     */
51316     autoExpandMax : 1000,
51317
51318     /**
51319     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51320     */
51321     view : null,
51322
51323     /**
51324     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51325     */
51326     loadMask : false,
51327     /**
51328     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51329     */
51330     dropTarget: false,
51331     
51332    
51333     
51334     // private
51335     rendered : false,
51336
51337     /**
51338     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51339     * of a fixed width. Default is false.
51340     */
51341     /**
51342     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51343     */
51344     /**
51345      * Called once after all setup has been completed and the grid is ready to be rendered.
51346      * @return {Roo.grid.Grid} this
51347      */
51348     render : function()
51349     {
51350         var c = this.container;
51351         // try to detect autoHeight/width mode
51352         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51353             this.autoHeight = true;
51354         }
51355         var view = this.getView();
51356         view.init(this);
51357
51358         c.on("click", this.onClick, this);
51359         c.on("dblclick", this.onDblClick, this);
51360         c.on("contextmenu", this.onContextMenu, this);
51361         c.on("keydown", this.onKeyDown, this);
51362         if (Roo.isTouch) {
51363             c.on("touchstart", this.onTouchStart, this);
51364         }
51365
51366         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51367
51368         this.getSelectionModel().init(this);
51369
51370         view.render();
51371
51372         if(this.loadMask){
51373             this.loadMask = new Roo.LoadMask(this.container,
51374                     Roo.apply({store:this.dataSource}, this.loadMask));
51375         }
51376         
51377         
51378         if (this.toolbar && this.toolbar.xtype) {
51379             this.toolbar.container = this.getView().getHeaderPanel(true);
51380             this.toolbar = new Roo.Toolbar(this.toolbar);
51381         }
51382         if (this.footer && this.footer.xtype) {
51383             this.footer.dataSource = this.getDataSource();
51384             this.footer.container = this.getView().getFooterPanel(true);
51385             this.footer = Roo.factory(this.footer, Roo);
51386         }
51387         if (this.dropTarget && this.dropTarget.xtype) {
51388             delete this.dropTarget.xtype;
51389             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51390         }
51391         
51392         
51393         this.rendered = true;
51394         this.fireEvent('render', this);
51395         return this;
51396     },
51397
51398         /**
51399          * Reconfigures the grid to use a different Store and Column Model.
51400          * The View will be bound to the new objects and refreshed.
51401          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51402          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51403          */
51404     reconfigure : function(dataSource, colModel){
51405         if(this.loadMask){
51406             this.loadMask.destroy();
51407             this.loadMask = new Roo.LoadMask(this.container,
51408                     Roo.apply({store:dataSource}, this.loadMask));
51409         }
51410         this.view.bind(dataSource, colModel);
51411         this.dataSource = dataSource;
51412         this.colModel = colModel;
51413         this.view.refresh(true);
51414     },
51415
51416     // private
51417     onKeyDown : function(e){
51418         this.fireEvent("keydown", e);
51419     },
51420
51421     /**
51422      * Destroy this grid.
51423      * @param {Boolean} removeEl True to remove the element
51424      */
51425     destroy : function(removeEl, keepListeners){
51426         if(this.loadMask){
51427             this.loadMask.destroy();
51428         }
51429         var c = this.container;
51430         c.removeAllListeners();
51431         this.view.destroy();
51432         this.colModel.purgeListeners();
51433         if(!keepListeners){
51434             this.purgeListeners();
51435         }
51436         c.update("");
51437         if(removeEl === true){
51438             c.remove();
51439         }
51440     },
51441
51442     // private
51443     processEvent : function(name, e){
51444         // does this fire select???
51445         Roo.log('grid:processEvent '  + name);
51446         
51447         if (name != 'touchstart' ) {
51448             this.fireEvent(name, e);    
51449         }
51450         
51451         var t = e.getTarget();
51452         var v = this.view;
51453         var header = v.findHeaderIndex(t);
51454         if(header !== false){
51455             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51456         }else{
51457             var row = v.findRowIndex(t);
51458             var cell = v.findCellIndex(t);
51459             if (name == 'touchstart') {
51460                 // first touch is always a click.
51461                 // hopefull this happens after selection is updated.?
51462                 name = false;
51463                 
51464                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51465                     var cs = this.selModel.getSelectedCell();
51466                     if (row == cs[0] && cell == cs[1]){
51467                         name = 'dblclick';
51468                     }
51469                 }
51470                 if (typeof(this.selModel.getSelections) != 'undefined') {
51471                     var cs = this.selModel.getSelections();
51472                     var ds = this.dataSource;
51473                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51474                         name = 'dblclick';
51475                     }
51476                 }
51477                 if (!name) {
51478                     return;
51479                 }
51480             }
51481             
51482             
51483             if(row !== false){
51484                 this.fireEvent("row" + name, this, row, e);
51485                 if(cell !== false){
51486                     this.fireEvent("cell" + name, this, row, cell, e);
51487                 }
51488             }
51489         }
51490     },
51491
51492     // private
51493     onClick : function(e){
51494         this.processEvent("click", e);
51495     },
51496    // private
51497     onTouchStart : function(e){
51498         this.processEvent("touchstart", e);
51499     },
51500
51501     // private
51502     onContextMenu : function(e, t){
51503         this.processEvent("contextmenu", e);
51504     },
51505
51506     // private
51507     onDblClick : function(e){
51508         this.processEvent("dblclick", e);
51509     },
51510
51511     // private
51512     walkCells : function(row, col, step, fn, scope){
51513         var cm = this.colModel, clen = cm.getColumnCount();
51514         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51515         if(step < 0){
51516             if(col < 0){
51517                 row--;
51518                 first = false;
51519             }
51520             while(row >= 0){
51521                 if(!first){
51522                     col = clen-1;
51523                 }
51524                 first = false;
51525                 while(col >= 0){
51526                     if(fn.call(scope || this, row, col, cm) === true){
51527                         return [row, col];
51528                     }
51529                     col--;
51530                 }
51531                 row--;
51532             }
51533         } else {
51534             if(col >= clen){
51535                 row++;
51536                 first = false;
51537             }
51538             while(row < rlen){
51539                 if(!first){
51540                     col = 0;
51541                 }
51542                 first = false;
51543                 while(col < clen){
51544                     if(fn.call(scope || this, row, col, cm) === true){
51545                         return [row, col];
51546                     }
51547                     col++;
51548                 }
51549                 row++;
51550             }
51551         }
51552         return null;
51553     },
51554
51555     // private
51556     getSelections : function(){
51557         return this.selModel.getSelections();
51558     },
51559
51560     /**
51561      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51562      * but if manual update is required this method will initiate it.
51563      */
51564     autoSize : function(){
51565         if(this.rendered){
51566             this.view.layout();
51567             if(this.view.adjustForScroll){
51568                 this.view.adjustForScroll();
51569             }
51570         }
51571     },
51572
51573     /**
51574      * Returns the grid's underlying element.
51575      * @return {Element} The element
51576      */
51577     getGridEl : function(){
51578         return this.container;
51579     },
51580
51581     // private for compatibility, overridden by editor grid
51582     stopEditing : function(){},
51583
51584     /**
51585      * Returns the grid's SelectionModel.
51586      * @return {SelectionModel}
51587      */
51588     getSelectionModel : function(){
51589         if(!this.selModel){
51590             this.selModel = new Roo.grid.RowSelectionModel();
51591         }
51592         return this.selModel;
51593     },
51594
51595     /**
51596      * Returns the grid's DataSource.
51597      * @return {DataSource}
51598      */
51599     getDataSource : function(){
51600         return this.dataSource;
51601     },
51602
51603     /**
51604      * Returns the grid's ColumnModel.
51605      * @return {ColumnModel}
51606      */
51607     getColumnModel : function(){
51608         return this.colModel;
51609     },
51610
51611     /**
51612      * Returns the grid's GridView object.
51613      * @return {GridView}
51614      */
51615     getView : function(){
51616         if(!this.view){
51617             this.view = new Roo.grid.GridView(this.viewConfig);
51618         }
51619         return this.view;
51620     },
51621     /**
51622      * Called to get grid's drag proxy text, by default returns this.ddText.
51623      * @return {String}
51624      */
51625     getDragDropText : function(){
51626         var count = this.selModel.getCount();
51627         return String.format(this.ddText, count, count == 1 ? '' : 's');
51628     }
51629 });
51630 /**
51631  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51632  * %0 is replaced with the number of selected rows.
51633  * @type String
51634  */
51635 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51636  * Based on:
51637  * Ext JS Library 1.1.1
51638  * Copyright(c) 2006-2007, Ext JS, LLC.
51639  *
51640  * Originally Released Under LGPL - original licence link has changed is not relivant.
51641  *
51642  * Fork - LGPL
51643  * <script type="text/javascript">
51644  */
51645  
51646 Roo.grid.AbstractGridView = function(){
51647         this.grid = null;
51648         
51649         this.events = {
51650             "beforerowremoved" : true,
51651             "beforerowsinserted" : true,
51652             "beforerefresh" : true,
51653             "rowremoved" : true,
51654             "rowsinserted" : true,
51655             "rowupdated" : true,
51656             "refresh" : true
51657         };
51658     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51659 };
51660
51661 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51662     rowClass : "x-grid-row",
51663     cellClass : "x-grid-cell",
51664     tdClass : "x-grid-td",
51665     hdClass : "x-grid-hd",
51666     splitClass : "x-grid-hd-split",
51667     
51668         init: function(grid){
51669         this.grid = grid;
51670                 var cid = this.grid.getGridEl().id;
51671         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51672         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51673         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51674         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51675         },
51676         
51677         getColumnRenderers : function(){
51678         var renderers = [];
51679         var cm = this.grid.colModel;
51680         var colCount = cm.getColumnCount();
51681         for(var i = 0; i < colCount; i++){
51682             renderers[i] = cm.getRenderer(i);
51683         }
51684         return renderers;
51685     },
51686     
51687     getColumnIds : function(){
51688         var ids = [];
51689         var cm = this.grid.colModel;
51690         var colCount = cm.getColumnCount();
51691         for(var i = 0; i < colCount; i++){
51692             ids[i] = cm.getColumnId(i);
51693         }
51694         return ids;
51695     },
51696     
51697     getDataIndexes : function(){
51698         if(!this.indexMap){
51699             this.indexMap = this.buildIndexMap();
51700         }
51701         return this.indexMap.colToData;
51702     },
51703     
51704     getColumnIndexByDataIndex : function(dataIndex){
51705         if(!this.indexMap){
51706             this.indexMap = this.buildIndexMap();
51707         }
51708         return this.indexMap.dataToCol[dataIndex];
51709     },
51710     
51711     /**
51712      * Set a css style for a column dynamically. 
51713      * @param {Number} colIndex The index of the column
51714      * @param {String} name The css property name
51715      * @param {String} value The css value
51716      */
51717     setCSSStyle : function(colIndex, name, value){
51718         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51719         Roo.util.CSS.updateRule(selector, name, value);
51720     },
51721     
51722     generateRules : function(cm){
51723         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51724         Roo.util.CSS.removeStyleSheet(rulesId);
51725         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51726             var cid = cm.getColumnId(i);
51727             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51728                          this.tdSelector, cid, " {\n}\n",
51729                          this.hdSelector, cid, " {\n}\n",
51730                          this.splitSelector, cid, " {\n}\n");
51731         }
51732         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51733     }
51734 });/*
51735  * Based on:
51736  * Ext JS Library 1.1.1
51737  * Copyright(c) 2006-2007, Ext JS, LLC.
51738  *
51739  * Originally Released Under LGPL - original licence link has changed is not relivant.
51740  *
51741  * Fork - LGPL
51742  * <script type="text/javascript">
51743  */
51744
51745 // private
51746 // This is a support class used internally by the Grid components
51747 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51748     this.grid = grid;
51749     this.view = grid.getView();
51750     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51751     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51752     if(hd2){
51753         this.setHandleElId(Roo.id(hd));
51754         this.setOuterHandleElId(Roo.id(hd2));
51755     }
51756     this.scroll = false;
51757 };
51758 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51759     maxDragWidth: 120,
51760     getDragData : function(e){
51761         var t = Roo.lib.Event.getTarget(e);
51762         var h = this.view.findHeaderCell(t);
51763         if(h){
51764             return {ddel: h.firstChild, header:h};
51765         }
51766         return false;
51767     },
51768
51769     onInitDrag : function(e){
51770         this.view.headersDisabled = true;
51771         var clone = this.dragData.ddel.cloneNode(true);
51772         clone.id = Roo.id();
51773         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51774         this.proxy.update(clone);
51775         return true;
51776     },
51777
51778     afterValidDrop : function(){
51779         var v = this.view;
51780         setTimeout(function(){
51781             v.headersDisabled = false;
51782         }, 50);
51783     },
51784
51785     afterInvalidDrop : function(){
51786         var v = this.view;
51787         setTimeout(function(){
51788             v.headersDisabled = false;
51789         }, 50);
51790     }
51791 });
51792 /*
51793  * Based on:
51794  * Ext JS Library 1.1.1
51795  * Copyright(c) 2006-2007, Ext JS, LLC.
51796  *
51797  * Originally Released Under LGPL - original licence link has changed is not relivant.
51798  *
51799  * Fork - LGPL
51800  * <script type="text/javascript">
51801  */
51802 // private
51803 // This is a support class used internally by the Grid components
51804 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51805     this.grid = grid;
51806     this.view = grid.getView();
51807     // split the proxies so they don't interfere with mouse events
51808     this.proxyTop = Roo.DomHelper.append(document.body, {
51809         cls:"col-move-top", html:"&#160;"
51810     }, true);
51811     this.proxyBottom = Roo.DomHelper.append(document.body, {
51812         cls:"col-move-bottom", html:"&#160;"
51813     }, true);
51814     this.proxyTop.hide = this.proxyBottom.hide = function(){
51815         this.setLeftTop(-100,-100);
51816         this.setStyle("visibility", "hidden");
51817     };
51818     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51819     // temporarily disabled
51820     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51821     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51822 };
51823 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51824     proxyOffsets : [-4, -9],
51825     fly: Roo.Element.fly,
51826
51827     getTargetFromEvent : function(e){
51828         var t = Roo.lib.Event.getTarget(e);
51829         var cindex = this.view.findCellIndex(t);
51830         if(cindex !== false){
51831             return this.view.getHeaderCell(cindex);
51832         }
51833         return null;
51834     },
51835
51836     nextVisible : function(h){
51837         var v = this.view, cm = this.grid.colModel;
51838         h = h.nextSibling;
51839         while(h){
51840             if(!cm.isHidden(v.getCellIndex(h))){
51841                 return h;
51842             }
51843             h = h.nextSibling;
51844         }
51845         return null;
51846     },
51847
51848     prevVisible : function(h){
51849         var v = this.view, cm = this.grid.colModel;
51850         h = h.prevSibling;
51851         while(h){
51852             if(!cm.isHidden(v.getCellIndex(h))){
51853                 return h;
51854             }
51855             h = h.prevSibling;
51856         }
51857         return null;
51858     },
51859
51860     positionIndicator : function(h, n, e){
51861         var x = Roo.lib.Event.getPageX(e);
51862         var r = Roo.lib.Dom.getRegion(n.firstChild);
51863         var px, pt, py = r.top + this.proxyOffsets[1];
51864         if((r.right - x) <= (r.right-r.left)/2){
51865             px = r.right+this.view.borderWidth;
51866             pt = "after";
51867         }else{
51868             px = r.left;
51869             pt = "before";
51870         }
51871         var oldIndex = this.view.getCellIndex(h);
51872         var newIndex = this.view.getCellIndex(n);
51873
51874         if(this.grid.colModel.isFixed(newIndex)){
51875             return false;
51876         }
51877
51878         var locked = this.grid.colModel.isLocked(newIndex);
51879
51880         if(pt == "after"){
51881             newIndex++;
51882         }
51883         if(oldIndex < newIndex){
51884             newIndex--;
51885         }
51886         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51887             return false;
51888         }
51889         px +=  this.proxyOffsets[0];
51890         this.proxyTop.setLeftTop(px, py);
51891         this.proxyTop.show();
51892         if(!this.bottomOffset){
51893             this.bottomOffset = this.view.mainHd.getHeight();
51894         }
51895         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51896         this.proxyBottom.show();
51897         return pt;
51898     },
51899
51900     onNodeEnter : function(n, dd, e, data){
51901         if(data.header != n){
51902             this.positionIndicator(data.header, n, e);
51903         }
51904     },
51905
51906     onNodeOver : function(n, dd, e, data){
51907         var result = false;
51908         if(data.header != n){
51909             result = this.positionIndicator(data.header, n, e);
51910         }
51911         if(!result){
51912             this.proxyTop.hide();
51913             this.proxyBottom.hide();
51914         }
51915         return result ? this.dropAllowed : this.dropNotAllowed;
51916     },
51917
51918     onNodeOut : function(n, dd, e, data){
51919         this.proxyTop.hide();
51920         this.proxyBottom.hide();
51921     },
51922
51923     onNodeDrop : function(n, dd, e, data){
51924         var h = data.header;
51925         if(h != n){
51926             var cm = this.grid.colModel;
51927             var x = Roo.lib.Event.getPageX(e);
51928             var r = Roo.lib.Dom.getRegion(n.firstChild);
51929             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
51930             var oldIndex = this.view.getCellIndex(h);
51931             var newIndex = this.view.getCellIndex(n);
51932             var locked = cm.isLocked(newIndex);
51933             if(pt == "after"){
51934                 newIndex++;
51935             }
51936             if(oldIndex < newIndex){
51937                 newIndex--;
51938             }
51939             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
51940                 return false;
51941             }
51942             cm.setLocked(oldIndex, locked, true);
51943             cm.moveColumn(oldIndex, newIndex);
51944             this.grid.fireEvent("columnmove", oldIndex, newIndex);
51945             return true;
51946         }
51947         return false;
51948     }
51949 });
51950 /*
51951  * Based on:
51952  * Ext JS Library 1.1.1
51953  * Copyright(c) 2006-2007, Ext JS, LLC.
51954  *
51955  * Originally Released Under LGPL - original licence link has changed is not relivant.
51956  *
51957  * Fork - LGPL
51958  * <script type="text/javascript">
51959  */
51960   
51961 /**
51962  * @class Roo.grid.GridView
51963  * @extends Roo.util.Observable
51964  *
51965  * @constructor
51966  * @param {Object} config
51967  */
51968 Roo.grid.GridView = function(config){
51969     Roo.grid.GridView.superclass.constructor.call(this);
51970     this.el = null;
51971
51972     Roo.apply(this, config);
51973 };
51974
51975 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
51976
51977     unselectable :  'unselectable="on"',
51978     unselectableCls :  'x-unselectable',
51979     
51980     
51981     rowClass : "x-grid-row",
51982
51983     cellClass : "x-grid-col",
51984
51985     tdClass : "x-grid-td",
51986
51987     hdClass : "x-grid-hd",
51988
51989     splitClass : "x-grid-split",
51990
51991     sortClasses : ["sort-asc", "sort-desc"],
51992
51993     enableMoveAnim : false,
51994
51995     hlColor: "C3DAF9",
51996
51997     dh : Roo.DomHelper,
51998
51999     fly : Roo.Element.fly,
52000
52001     css : Roo.util.CSS,
52002
52003     borderWidth: 1,
52004
52005     splitOffset: 3,
52006
52007     scrollIncrement : 22,
52008
52009     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52010
52011     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52012
52013     bind : function(ds, cm){
52014         if(this.ds){
52015             this.ds.un("load", this.onLoad, this);
52016             this.ds.un("datachanged", this.onDataChange, this);
52017             this.ds.un("add", this.onAdd, this);
52018             this.ds.un("remove", this.onRemove, this);
52019             this.ds.un("update", this.onUpdate, this);
52020             this.ds.un("clear", this.onClear, this);
52021         }
52022         if(ds){
52023             ds.on("load", this.onLoad, this);
52024             ds.on("datachanged", this.onDataChange, this);
52025             ds.on("add", this.onAdd, this);
52026             ds.on("remove", this.onRemove, this);
52027             ds.on("update", this.onUpdate, this);
52028             ds.on("clear", this.onClear, this);
52029         }
52030         this.ds = ds;
52031
52032         if(this.cm){
52033             this.cm.un("widthchange", this.onColWidthChange, this);
52034             this.cm.un("headerchange", this.onHeaderChange, this);
52035             this.cm.un("hiddenchange", this.onHiddenChange, this);
52036             this.cm.un("columnmoved", this.onColumnMove, this);
52037             this.cm.un("columnlockchange", this.onColumnLock, this);
52038         }
52039         if(cm){
52040             this.generateRules(cm);
52041             cm.on("widthchange", this.onColWidthChange, this);
52042             cm.on("headerchange", this.onHeaderChange, this);
52043             cm.on("hiddenchange", this.onHiddenChange, this);
52044             cm.on("columnmoved", this.onColumnMove, this);
52045             cm.on("columnlockchange", this.onColumnLock, this);
52046         }
52047         this.cm = cm;
52048     },
52049
52050     init: function(grid){
52051         Roo.grid.GridView.superclass.init.call(this, grid);
52052
52053         this.bind(grid.dataSource, grid.colModel);
52054
52055         grid.on("headerclick", this.handleHeaderClick, this);
52056
52057         if(grid.trackMouseOver){
52058             grid.on("mouseover", this.onRowOver, this);
52059             grid.on("mouseout", this.onRowOut, this);
52060         }
52061         grid.cancelTextSelection = function(){};
52062         this.gridId = grid.id;
52063
52064         var tpls = this.templates || {};
52065
52066         if(!tpls.master){
52067             tpls.master = new Roo.Template(
52068                '<div class="x-grid" hidefocus="true">',
52069                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52070                   '<div class="x-grid-topbar"></div>',
52071                   '<div class="x-grid-scroller"><div></div></div>',
52072                   '<div class="x-grid-locked">',
52073                       '<div class="x-grid-header">{lockedHeader}</div>',
52074                       '<div class="x-grid-body">{lockedBody}</div>',
52075                   "</div>",
52076                   '<div class="x-grid-viewport">',
52077                       '<div class="x-grid-header">{header}</div>',
52078                       '<div class="x-grid-body">{body}</div>',
52079                   "</div>",
52080                   '<div class="x-grid-bottombar"></div>',
52081                  
52082                   '<div class="x-grid-resize-proxy">&#160;</div>',
52083                "</div>"
52084             );
52085             tpls.master.disableformats = true;
52086         }
52087
52088         if(!tpls.header){
52089             tpls.header = new Roo.Template(
52090                '<table border="0" cellspacing="0" cellpadding="0">',
52091                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52092                "</table>{splits}"
52093             );
52094             tpls.header.disableformats = true;
52095         }
52096         tpls.header.compile();
52097
52098         if(!tpls.hcell){
52099             tpls.hcell = new Roo.Template(
52100                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52101                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52102                 "</div></td>"
52103              );
52104              tpls.hcell.disableFormats = true;
52105         }
52106         tpls.hcell.compile();
52107
52108         if(!tpls.hsplit){
52109             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52110                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52111             tpls.hsplit.disableFormats = true;
52112         }
52113         tpls.hsplit.compile();
52114
52115         if(!tpls.body){
52116             tpls.body = new Roo.Template(
52117                '<table border="0" cellspacing="0" cellpadding="0">',
52118                "<tbody>{rows}</tbody>",
52119                "</table>"
52120             );
52121             tpls.body.disableFormats = true;
52122         }
52123         tpls.body.compile();
52124
52125         if(!tpls.row){
52126             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52127             tpls.row.disableFormats = true;
52128         }
52129         tpls.row.compile();
52130
52131         if(!tpls.cell){
52132             tpls.cell = new Roo.Template(
52133                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52134                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52135                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52136                 "</td>"
52137             );
52138             tpls.cell.disableFormats = true;
52139         }
52140         tpls.cell.compile();
52141
52142         this.templates = tpls;
52143     },
52144
52145     // remap these for backwards compat
52146     onColWidthChange : function(){
52147         this.updateColumns.apply(this, arguments);
52148     },
52149     onHeaderChange : function(){
52150         this.updateHeaders.apply(this, arguments);
52151     }, 
52152     onHiddenChange : function(){
52153         this.handleHiddenChange.apply(this, arguments);
52154     },
52155     onColumnMove : function(){
52156         this.handleColumnMove.apply(this, arguments);
52157     },
52158     onColumnLock : function(){
52159         this.handleLockChange.apply(this, arguments);
52160     },
52161
52162     onDataChange : function(){
52163         this.refresh();
52164         this.updateHeaderSortState();
52165     },
52166
52167     onClear : function(){
52168         this.refresh();
52169     },
52170
52171     onUpdate : function(ds, record){
52172         this.refreshRow(record);
52173     },
52174
52175     refreshRow : function(record){
52176         var ds = this.ds, index;
52177         if(typeof record == 'number'){
52178             index = record;
52179             record = ds.getAt(index);
52180         }else{
52181             index = ds.indexOf(record);
52182         }
52183         this.insertRows(ds, index, index, true);
52184         this.onRemove(ds, record, index+1, true);
52185         this.syncRowHeights(index, index);
52186         this.layout();
52187         this.fireEvent("rowupdated", this, index, record);
52188     },
52189
52190     onAdd : function(ds, records, index){
52191         this.insertRows(ds, index, index + (records.length-1));
52192     },
52193
52194     onRemove : function(ds, record, index, isUpdate){
52195         if(isUpdate !== true){
52196             this.fireEvent("beforerowremoved", this, index, record);
52197         }
52198         var bt = this.getBodyTable(), lt = this.getLockedTable();
52199         if(bt.rows[index]){
52200             bt.firstChild.removeChild(bt.rows[index]);
52201         }
52202         if(lt.rows[index]){
52203             lt.firstChild.removeChild(lt.rows[index]);
52204         }
52205         if(isUpdate !== true){
52206             this.stripeRows(index);
52207             this.syncRowHeights(index, index);
52208             this.layout();
52209             this.fireEvent("rowremoved", this, index, record);
52210         }
52211     },
52212
52213     onLoad : function(){
52214         this.scrollToTop();
52215     },
52216
52217     /**
52218      * Scrolls the grid to the top
52219      */
52220     scrollToTop : function(){
52221         if(this.scroller){
52222             this.scroller.dom.scrollTop = 0;
52223             this.syncScroll();
52224         }
52225     },
52226
52227     /**
52228      * Gets a panel in the header of the grid that can be used for toolbars etc.
52229      * After modifying the contents of this panel a call to grid.autoSize() may be
52230      * required to register any changes in size.
52231      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52232      * @return Roo.Element
52233      */
52234     getHeaderPanel : function(doShow){
52235         if(doShow){
52236             this.headerPanel.show();
52237         }
52238         return this.headerPanel;
52239     },
52240
52241     /**
52242      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52243      * After modifying the contents of this panel a call to grid.autoSize() may be
52244      * required to register any changes in size.
52245      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52246      * @return Roo.Element
52247      */
52248     getFooterPanel : function(doShow){
52249         if(doShow){
52250             this.footerPanel.show();
52251         }
52252         return this.footerPanel;
52253     },
52254
52255     initElements : function(){
52256         var E = Roo.Element;
52257         var el = this.grid.getGridEl().dom.firstChild;
52258         var cs = el.childNodes;
52259
52260         this.el = new E(el);
52261         
52262          this.focusEl = new E(el.firstChild);
52263         this.focusEl.swallowEvent("click", true);
52264         
52265         this.headerPanel = new E(cs[1]);
52266         this.headerPanel.enableDisplayMode("block");
52267
52268         this.scroller = new E(cs[2]);
52269         this.scrollSizer = new E(this.scroller.dom.firstChild);
52270
52271         this.lockedWrap = new E(cs[3]);
52272         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52273         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52274
52275         this.mainWrap = new E(cs[4]);
52276         this.mainHd = new E(this.mainWrap.dom.firstChild);
52277         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52278
52279         this.footerPanel = new E(cs[5]);
52280         this.footerPanel.enableDisplayMode("block");
52281
52282         this.resizeProxy = new E(cs[6]);
52283
52284         this.headerSelector = String.format(
52285            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52286            this.lockedHd.id, this.mainHd.id
52287         );
52288
52289         this.splitterSelector = String.format(
52290            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52291            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52292         );
52293     },
52294     idToCssName : function(s)
52295     {
52296         return s.replace(/[^a-z0-9]+/ig, '-');
52297     },
52298
52299     getHeaderCell : function(index){
52300         return Roo.DomQuery.select(this.headerSelector)[index];
52301     },
52302
52303     getHeaderCellMeasure : function(index){
52304         return this.getHeaderCell(index).firstChild;
52305     },
52306
52307     getHeaderCellText : function(index){
52308         return this.getHeaderCell(index).firstChild.firstChild;
52309     },
52310
52311     getLockedTable : function(){
52312         return this.lockedBody.dom.firstChild;
52313     },
52314
52315     getBodyTable : function(){
52316         return this.mainBody.dom.firstChild;
52317     },
52318
52319     getLockedRow : function(index){
52320         return this.getLockedTable().rows[index];
52321     },
52322
52323     getRow : function(index){
52324         return this.getBodyTable().rows[index];
52325     },
52326
52327     getRowComposite : function(index){
52328         if(!this.rowEl){
52329             this.rowEl = new Roo.CompositeElementLite();
52330         }
52331         var els = [], lrow, mrow;
52332         if(lrow = this.getLockedRow(index)){
52333             els.push(lrow);
52334         }
52335         if(mrow = this.getRow(index)){
52336             els.push(mrow);
52337         }
52338         this.rowEl.elements = els;
52339         return this.rowEl;
52340     },
52341     /**
52342      * Gets the 'td' of the cell
52343      * 
52344      * @param {Integer} rowIndex row to select
52345      * @param {Integer} colIndex column to select
52346      * 
52347      * @return {Object} 
52348      */
52349     getCell : function(rowIndex, colIndex){
52350         var locked = this.cm.getLockedCount();
52351         var source;
52352         if(colIndex < locked){
52353             source = this.lockedBody.dom.firstChild;
52354         }else{
52355             source = this.mainBody.dom.firstChild;
52356             colIndex -= locked;
52357         }
52358         return source.rows[rowIndex].childNodes[colIndex];
52359     },
52360
52361     getCellText : function(rowIndex, colIndex){
52362         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52363     },
52364
52365     getCellBox : function(cell){
52366         var b = this.fly(cell).getBox();
52367         if(Roo.isOpera){ // opera fails to report the Y
52368             b.y = cell.offsetTop + this.mainBody.getY();
52369         }
52370         return b;
52371     },
52372
52373     getCellIndex : function(cell){
52374         var id = String(cell.className).match(this.cellRE);
52375         if(id){
52376             return parseInt(id[1], 10);
52377         }
52378         return 0;
52379     },
52380
52381     findHeaderIndex : function(n){
52382         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52383         return r ? this.getCellIndex(r) : false;
52384     },
52385
52386     findHeaderCell : function(n){
52387         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52388         return r ? r : false;
52389     },
52390
52391     findRowIndex : function(n){
52392         if(!n){
52393             return false;
52394         }
52395         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52396         return r ? r.rowIndex : false;
52397     },
52398
52399     findCellIndex : function(node){
52400         var stop = this.el.dom;
52401         while(node && node != stop){
52402             if(this.findRE.test(node.className)){
52403                 return this.getCellIndex(node);
52404             }
52405             node = node.parentNode;
52406         }
52407         return false;
52408     },
52409
52410     getColumnId : function(index){
52411         return this.cm.getColumnId(index);
52412     },
52413
52414     getSplitters : function()
52415     {
52416         if(this.splitterSelector){
52417            return Roo.DomQuery.select(this.splitterSelector);
52418         }else{
52419             return null;
52420       }
52421     },
52422
52423     getSplitter : function(index){
52424         return this.getSplitters()[index];
52425     },
52426
52427     onRowOver : function(e, t){
52428         var row;
52429         if((row = this.findRowIndex(t)) !== false){
52430             this.getRowComposite(row).addClass("x-grid-row-over");
52431         }
52432     },
52433
52434     onRowOut : function(e, t){
52435         var row;
52436         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52437             this.getRowComposite(row).removeClass("x-grid-row-over");
52438         }
52439     },
52440
52441     renderHeaders : function(){
52442         var cm = this.cm;
52443         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52444         var cb = [], lb = [], sb = [], lsb = [], p = {};
52445         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52446             p.cellId = "x-grid-hd-0-" + i;
52447             p.splitId = "x-grid-csplit-0-" + i;
52448             p.id = cm.getColumnId(i);
52449             p.title = cm.getColumnTooltip(i) || "";
52450             p.value = cm.getColumnHeader(i) || "";
52451             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52452             if(!cm.isLocked(i)){
52453                 cb[cb.length] = ct.apply(p);
52454                 sb[sb.length] = st.apply(p);
52455             }else{
52456                 lb[lb.length] = ct.apply(p);
52457                 lsb[lsb.length] = st.apply(p);
52458             }
52459         }
52460         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52461                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52462     },
52463
52464     updateHeaders : function(){
52465         var html = this.renderHeaders();
52466         this.lockedHd.update(html[0]);
52467         this.mainHd.update(html[1]);
52468     },
52469
52470     /**
52471      * Focuses the specified row.
52472      * @param {Number} row The row index
52473      */
52474     focusRow : function(row)
52475     {
52476         //Roo.log('GridView.focusRow');
52477         var x = this.scroller.dom.scrollLeft;
52478         this.focusCell(row, 0, false);
52479         this.scroller.dom.scrollLeft = x;
52480     },
52481
52482     /**
52483      * Focuses the specified cell.
52484      * @param {Number} row The row index
52485      * @param {Number} col The column index
52486      * @param {Boolean} hscroll false to disable horizontal scrolling
52487      */
52488     focusCell : function(row, col, hscroll)
52489     {
52490         //Roo.log('GridView.focusCell');
52491         var el = this.ensureVisible(row, col, hscroll);
52492         this.focusEl.alignTo(el, "tl-tl");
52493         if(Roo.isGecko){
52494             this.focusEl.focus();
52495         }else{
52496             this.focusEl.focus.defer(1, this.focusEl);
52497         }
52498     },
52499
52500     /**
52501      * Scrolls the specified cell into view
52502      * @param {Number} row The row index
52503      * @param {Number} col The column index
52504      * @param {Boolean} hscroll false to disable horizontal scrolling
52505      */
52506     ensureVisible : function(row, col, hscroll)
52507     {
52508         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52509         //return null; //disable for testing.
52510         if(typeof row != "number"){
52511             row = row.rowIndex;
52512         }
52513         if(row < 0 && row >= this.ds.getCount()){
52514             return  null;
52515         }
52516         col = (col !== undefined ? col : 0);
52517         var cm = this.grid.colModel;
52518         while(cm.isHidden(col)){
52519             col++;
52520         }
52521
52522         var el = this.getCell(row, col);
52523         if(!el){
52524             return null;
52525         }
52526         var c = this.scroller.dom;
52527
52528         var ctop = parseInt(el.offsetTop, 10);
52529         var cleft = parseInt(el.offsetLeft, 10);
52530         var cbot = ctop + el.offsetHeight;
52531         var cright = cleft + el.offsetWidth;
52532         
52533         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52534         var stop = parseInt(c.scrollTop, 10);
52535         var sleft = parseInt(c.scrollLeft, 10);
52536         var sbot = stop + ch;
52537         var sright = sleft + c.clientWidth;
52538         /*
52539         Roo.log('GridView.ensureVisible:' +
52540                 ' ctop:' + ctop +
52541                 ' c.clientHeight:' + c.clientHeight +
52542                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52543                 ' stop:' + stop +
52544                 ' cbot:' + cbot +
52545                 ' sbot:' + sbot +
52546                 ' ch:' + ch  
52547                 );
52548         */
52549         if(ctop < stop){
52550              c.scrollTop = ctop;
52551             //Roo.log("set scrolltop to ctop DISABLE?");
52552         }else if(cbot > sbot){
52553             //Roo.log("set scrolltop to cbot-ch");
52554             c.scrollTop = cbot-ch;
52555         }
52556         
52557         if(hscroll !== false){
52558             if(cleft < sleft){
52559                 c.scrollLeft = cleft;
52560             }else if(cright > sright){
52561                 c.scrollLeft = cright-c.clientWidth;
52562             }
52563         }
52564          
52565         return el;
52566     },
52567
52568     updateColumns : function(){
52569         this.grid.stopEditing();
52570         var cm = this.grid.colModel, colIds = this.getColumnIds();
52571         //var totalWidth = cm.getTotalWidth();
52572         var pos = 0;
52573         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52574             //if(cm.isHidden(i)) continue;
52575             var w = cm.getColumnWidth(i);
52576             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52577             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52578         }
52579         this.updateSplitters();
52580     },
52581
52582     generateRules : function(cm){
52583         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52584         Roo.util.CSS.removeStyleSheet(rulesId);
52585         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52586             var cid = cm.getColumnId(i);
52587             var align = '';
52588             if(cm.config[i].align){
52589                 align = 'text-align:'+cm.config[i].align+';';
52590             }
52591             var hidden = '';
52592             if(cm.isHidden(i)){
52593                 hidden = 'display:none;';
52594             }
52595             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52596             ruleBuf.push(
52597                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52598                     this.hdSelector, cid, " {\n", align, width, "}\n",
52599                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52600                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52601         }
52602         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52603     },
52604
52605     updateSplitters : function(){
52606         var cm = this.cm, s = this.getSplitters();
52607         if(s){ // splitters not created yet
52608             var pos = 0, locked = true;
52609             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52610                 if(cm.isHidden(i)) continue;
52611                 var w = cm.getColumnWidth(i); // make sure it's a number
52612                 if(!cm.isLocked(i) && locked){
52613                     pos = 0;
52614                     locked = false;
52615                 }
52616                 pos += w;
52617                 s[i].style.left = (pos-this.splitOffset) + "px";
52618             }
52619         }
52620     },
52621
52622     handleHiddenChange : function(colModel, colIndex, hidden){
52623         if(hidden){
52624             this.hideColumn(colIndex);
52625         }else{
52626             this.unhideColumn(colIndex);
52627         }
52628     },
52629
52630     hideColumn : function(colIndex){
52631         var cid = this.getColumnId(colIndex);
52632         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52633         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52634         if(Roo.isSafari){
52635             this.updateHeaders();
52636         }
52637         this.updateSplitters();
52638         this.layout();
52639     },
52640
52641     unhideColumn : function(colIndex){
52642         var cid = this.getColumnId(colIndex);
52643         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52644         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52645
52646         if(Roo.isSafari){
52647             this.updateHeaders();
52648         }
52649         this.updateSplitters();
52650         this.layout();
52651     },
52652
52653     insertRows : function(dm, firstRow, lastRow, isUpdate){
52654         if(firstRow == 0 && lastRow == dm.getCount()-1){
52655             this.refresh();
52656         }else{
52657             if(!isUpdate){
52658                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52659             }
52660             var s = this.getScrollState();
52661             var markup = this.renderRows(firstRow, lastRow);
52662             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52663             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52664             this.restoreScroll(s);
52665             if(!isUpdate){
52666                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52667                 this.syncRowHeights(firstRow, lastRow);
52668                 this.stripeRows(firstRow);
52669                 this.layout();
52670             }
52671         }
52672     },
52673
52674     bufferRows : function(markup, target, index){
52675         var before = null, trows = target.rows, tbody = target.tBodies[0];
52676         if(index < trows.length){
52677             before = trows[index];
52678         }
52679         var b = document.createElement("div");
52680         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52681         var rows = b.firstChild.rows;
52682         for(var i = 0, len = rows.length; i < len; i++){
52683             if(before){
52684                 tbody.insertBefore(rows[0], before);
52685             }else{
52686                 tbody.appendChild(rows[0]);
52687             }
52688         }
52689         b.innerHTML = "";
52690         b = null;
52691     },
52692
52693     deleteRows : function(dm, firstRow, lastRow){
52694         if(dm.getRowCount()<1){
52695             this.fireEvent("beforerefresh", this);
52696             this.mainBody.update("");
52697             this.lockedBody.update("");
52698             this.fireEvent("refresh", this);
52699         }else{
52700             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52701             var bt = this.getBodyTable();
52702             var tbody = bt.firstChild;
52703             var rows = bt.rows;
52704             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52705                 tbody.removeChild(rows[firstRow]);
52706             }
52707             this.stripeRows(firstRow);
52708             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52709         }
52710     },
52711
52712     updateRows : function(dataSource, firstRow, lastRow){
52713         var s = this.getScrollState();
52714         this.refresh();
52715         this.restoreScroll(s);
52716     },
52717
52718     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52719         if(!noRefresh){
52720            this.refresh();
52721         }
52722         this.updateHeaderSortState();
52723     },
52724
52725     getScrollState : function(){
52726         
52727         var sb = this.scroller.dom;
52728         return {left: sb.scrollLeft, top: sb.scrollTop};
52729     },
52730
52731     stripeRows : function(startRow){
52732         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52733             return;
52734         }
52735         startRow = startRow || 0;
52736         var rows = this.getBodyTable().rows;
52737         var lrows = this.getLockedTable().rows;
52738         var cls = ' x-grid-row-alt ';
52739         for(var i = startRow, len = rows.length; i < len; i++){
52740             var row = rows[i], lrow = lrows[i];
52741             var isAlt = ((i+1) % 2 == 0);
52742             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52743             if(isAlt == hasAlt){
52744                 continue;
52745             }
52746             if(isAlt){
52747                 row.className += " x-grid-row-alt";
52748             }else{
52749                 row.className = row.className.replace("x-grid-row-alt", "");
52750             }
52751             if(lrow){
52752                 lrow.className = row.className;
52753             }
52754         }
52755     },
52756
52757     restoreScroll : function(state){
52758         //Roo.log('GridView.restoreScroll');
52759         var sb = this.scroller.dom;
52760         sb.scrollLeft = state.left;
52761         sb.scrollTop = state.top;
52762         this.syncScroll();
52763     },
52764
52765     syncScroll : function(){
52766         //Roo.log('GridView.syncScroll');
52767         var sb = this.scroller.dom;
52768         var sh = this.mainHd.dom;
52769         var bs = this.mainBody.dom;
52770         var lv = this.lockedBody.dom;
52771         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52772         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52773     },
52774
52775     handleScroll : function(e){
52776         this.syncScroll();
52777         var sb = this.scroller.dom;
52778         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52779         e.stopEvent();
52780     },
52781
52782     handleWheel : function(e){
52783         var d = e.getWheelDelta();
52784         this.scroller.dom.scrollTop -= d*22;
52785         // set this here to prevent jumpy scrolling on large tables
52786         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52787         e.stopEvent();
52788     },
52789
52790     renderRows : function(startRow, endRow){
52791         // pull in all the crap needed to render rows
52792         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52793         var colCount = cm.getColumnCount();
52794
52795         if(ds.getCount() < 1){
52796             return ["", ""];
52797         }
52798
52799         // build a map for all the columns
52800         var cs = [];
52801         for(var i = 0; i < colCount; i++){
52802             var name = cm.getDataIndex(i);
52803             cs[i] = {
52804                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52805                 renderer : cm.getRenderer(i),
52806                 id : cm.getColumnId(i),
52807                 locked : cm.isLocked(i)
52808             };
52809         }
52810
52811         startRow = startRow || 0;
52812         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52813
52814         // records to render
52815         var rs = ds.getRange(startRow, endRow);
52816
52817         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52818     },
52819
52820     // As much as I hate to duplicate code, this was branched because FireFox really hates
52821     // [].join("") on strings. The performance difference was substantial enough to
52822     // branch this function
52823     doRender : Roo.isGecko ?
52824             function(cs, rs, ds, startRow, colCount, stripe){
52825                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52826                 // buffers
52827                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52828                 
52829                 var hasListener = this.grid.hasListener('rowclass');
52830                 var rowcfg = {};
52831                 for(var j = 0, len = rs.length; j < len; j++){
52832                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52833                     for(var i = 0; i < colCount; i++){
52834                         c = cs[i];
52835                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52836                         p.id = c.id;
52837                         p.css = p.attr = "";
52838                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52839                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52840                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52841                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52842                         }
52843                         var markup = ct.apply(p);
52844                         if(!c.locked){
52845                             cb+= markup;
52846                         }else{
52847                             lcb+= markup;
52848                         }
52849                     }
52850                     var alt = [];
52851                     if(stripe && ((rowIndex+1) % 2 == 0)){
52852                         alt.push("x-grid-row-alt")
52853                     }
52854                     if(r.dirty){
52855                         alt.push(  " x-grid-dirty-row");
52856                     }
52857                     rp.cells = lcb;
52858                     if(this.getRowClass){
52859                         alt.push(this.getRowClass(r, rowIndex));
52860                     }
52861                     if (hasListener) {
52862                         rowcfg = {
52863                              
52864                             record: r,
52865                             rowIndex : rowIndex,
52866                             rowClass : ''
52867                         }
52868                         this.grid.fireEvent('rowclass', this, rowcfg);
52869                         alt.push(rowcfg.rowClass);
52870                     }
52871                     rp.alt = alt.join(" ");
52872                     lbuf+= rt.apply(rp);
52873                     rp.cells = cb;
52874                     buf+=  rt.apply(rp);
52875                 }
52876                 return [lbuf, buf];
52877             } :
52878             function(cs, rs, ds, startRow, colCount, stripe){
52879                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52880                 // buffers
52881                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52882                 var hasListener = this.grid.hasListener('rowclass');
52883  
52884                 var rowcfg = {};
52885                 for(var j = 0, len = rs.length; j < len; j++){
52886                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52887                     for(var i = 0; i < colCount; i++){
52888                         c = cs[i];
52889                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52890                         p.id = c.id;
52891                         p.css = p.attr = "";
52892                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52893                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52894                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52895                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52896                         }
52897                         
52898                         var markup = ct.apply(p);
52899                         if(!c.locked){
52900                             cb[cb.length] = markup;
52901                         }else{
52902                             lcb[lcb.length] = markup;
52903                         }
52904                     }
52905                     var alt = [];
52906                     if(stripe && ((rowIndex+1) % 2 == 0)){
52907                         alt.push( "x-grid-row-alt");
52908                     }
52909                     if(r.dirty){
52910                         alt.push(" x-grid-dirty-row");
52911                     }
52912                     rp.cells = lcb;
52913                     if(this.getRowClass){
52914                         alt.push( this.getRowClass(r, rowIndex));
52915                     }
52916                     if (hasListener) {
52917                         rowcfg = {
52918                              
52919                             record: r,
52920                             rowIndex : rowIndex,
52921                             rowClass : ''
52922                         }
52923                         this.grid.fireEvent('rowclass', this, rowcfg);
52924                         alt.push(rowcfg.rowClass);
52925                     }
52926                     rp.alt = alt.join(" ");
52927                     rp.cells = lcb.join("");
52928                     lbuf[lbuf.length] = rt.apply(rp);
52929                     rp.cells = cb.join("");
52930                     buf[buf.length] =  rt.apply(rp);
52931                 }
52932                 return [lbuf.join(""), buf.join("")];
52933             },
52934
52935     renderBody : function(){
52936         var markup = this.renderRows();
52937         var bt = this.templates.body;
52938         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
52939     },
52940
52941     /**
52942      * Refreshes the grid
52943      * @param {Boolean} headersToo
52944      */
52945     refresh : function(headersToo){
52946         this.fireEvent("beforerefresh", this);
52947         this.grid.stopEditing();
52948         var result = this.renderBody();
52949         this.lockedBody.update(result[0]);
52950         this.mainBody.update(result[1]);
52951         if(headersToo === true){
52952             this.updateHeaders();
52953             this.updateColumns();
52954             this.updateSplitters();
52955             this.updateHeaderSortState();
52956         }
52957         this.syncRowHeights();
52958         this.layout();
52959         this.fireEvent("refresh", this);
52960     },
52961
52962     handleColumnMove : function(cm, oldIndex, newIndex){
52963         this.indexMap = null;
52964         var s = this.getScrollState();
52965         this.refresh(true);
52966         this.restoreScroll(s);
52967         this.afterMove(newIndex);
52968     },
52969
52970     afterMove : function(colIndex){
52971         if(this.enableMoveAnim && Roo.enableFx){
52972             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
52973         }
52974         // if multisort - fix sortOrder, and reload..
52975         if (this.grid.dataSource.multiSort) {
52976             // the we can call sort again..
52977             var dm = this.grid.dataSource;
52978             var cm = this.grid.colModel;
52979             var so = [];
52980             for(var i = 0; i < cm.config.length; i++ ) {
52981                 
52982                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
52983                     continue; // dont' bother, it's not in sort list or being set.
52984                 }
52985                 
52986                 so.push(cm.config[i].dataIndex);
52987             };
52988             dm.sortOrder = so;
52989             dm.load(dm.lastOptions);
52990             
52991             
52992         }
52993         
52994     },
52995
52996     updateCell : function(dm, rowIndex, dataIndex){
52997         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
52998         if(typeof colIndex == "undefined"){ // not present in grid
52999             return;
53000         }
53001         var cm = this.grid.colModel;
53002         var cell = this.getCell(rowIndex, colIndex);
53003         var cellText = this.getCellText(rowIndex, colIndex);
53004
53005         var p = {
53006             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53007             id : cm.getColumnId(colIndex),
53008             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53009         };
53010         var renderer = cm.getRenderer(colIndex);
53011         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53012         if(typeof val == "undefined" || val === "") val = "&#160;";
53013         cellText.innerHTML = val;
53014         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53015         this.syncRowHeights(rowIndex, rowIndex);
53016     },
53017
53018     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53019         var maxWidth = 0;
53020         if(this.grid.autoSizeHeaders){
53021             var h = this.getHeaderCellMeasure(colIndex);
53022             maxWidth = Math.max(maxWidth, h.scrollWidth);
53023         }
53024         var tb, index;
53025         if(this.cm.isLocked(colIndex)){
53026             tb = this.getLockedTable();
53027             index = colIndex;
53028         }else{
53029             tb = this.getBodyTable();
53030             index = colIndex - this.cm.getLockedCount();
53031         }
53032         if(tb && tb.rows){
53033             var rows = tb.rows;
53034             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53035             for(var i = 0; i < stopIndex; i++){
53036                 var cell = rows[i].childNodes[index].firstChild;
53037                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53038             }
53039         }
53040         return maxWidth + /*margin for error in IE*/ 5;
53041     },
53042     /**
53043      * Autofit a column to its content.
53044      * @param {Number} colIndex
53045      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53046      */
53047      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53048          if(this.cm.isHidden(colIndex)){
53049              return; // can't calc a hidden column
53050          }
53051         if(forceMinSize){
53052             var cid = this.cm.getColumnId(colIndex);
53053             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53054            if(this.grid.autoSizeHeaders){
53055                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53056            }
53057         }
53058         var newWidth = this.calcColumnWidth(colIndex);
53059         this.cm.setColumnWidth(colIndex,
53060             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53061         if(!suppressEvent){
53062             this.grid.fireEvent("columnresize", colIndex, newWidth);
53063         }
53064     },
53065
53066     /**
53067      * Autofits all columns to their content and then expands to fit any extra space in the grid
53068      */
53069      autoSizeColumns : function(){
53070         var cm = this.grid.colModel;
53071         var colCount = cm.getColumnCount();
53072         for(var i = 0; i < colCount; i++){
53073             this.autoSizeColumn(i, true, true);
53074         }
53075         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53076             this.fitColumns();
53077         }else{
53078             this.updateColumns();
53079             this.layout();
53080         }
53081     },
53082
53083     /**
53084      * Autofits all columns to the grid's width proportionate with their current size
53085      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53086      */
53087     fitColumns : function(reserveScrollSpace){
53088         var cm = this.grid.colModel;
53089         var colCount = cm.getColumnCount();
53090         var cols = [];
53091         var width = 0;
53092         var i, w;
53093         for (i = 0; i < colCount; i++){
53094             if(!cm.isHidden(i) && !cm.isFixed(i)){
53095                 w = cm.getColumnWidth(i);
53096                 cols.push(i);
53097                 cols.push(w);
53098                 width += w;
53099             }
53100         }
53101         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53102         if(reserveScrollSpace){
53103             avail -= 17;
53104         }
53105         var frac = (avail - cm.getTotalWidth())/width;
53106         while (cols.length){
53107             w = cols.pop();
53108             i = cols.pop();
53109             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53110         }
53111         this.updateColumns();
53112         this.layout();
53113     },
53114
53115     onRowSelect : function(rowIndex){
53116         var row = this.getRowComposite(rowIndex);
53117         row.addClass("x-grid-row-selected");
53118     },
53119
53120     onRowDeselect : function(rowIndex){
53121         var row = this.getRowComposite(rowIndex);
53122         row.removeClass("x-grid-row-selected");
53123     },
53124
53125     onCellSelect : function(row, col){
53126         var cell = this.getCell(row, col);
53127         if(cell){
53128             Roo.fly(cell).addClass("x-grid-cell-selected");
53129         }
53130     },
53131
53132     onCellDeselect : function(row, col){
53133         var cell = this.getCell(row, col);
53134         if(cell){
53135             Roo.fly(cell).removeClass("x-grid-cell-selected");
53136         }
53137     },
53138
53139     updateHeaderSortState : function(){
53140         
53141         // sort state can be single { field: xxx, direction : yyy}
53142         // or   { xxx=>ASC , yyy : DESC ..... }
53143         
53144         var mstate = {};
53145         if (!this.ds.multiSort) { 
53146             var state = this.ds.getSortState();
53147             if(!state){
53148                 return;
53149             }
53150             mstate[state.field] = state.direction;
53151             // FIXME... - this is not used here.. but might be elsewhere..
53152             this.sortState = state;
53153             
53154         } else {
53155             mstate = this.ds.sortToggle;
53156         }
53157         //remove existing sort classes..
53158         
53159         var sc = this.sortClasses;
53160         var hds = this.el.select(this.headerSelector).removeClass(sc);
53161         
53162         for(var f in mstate) {
53163         
53164             var sortColumn = this.cm.findColumnIndex(f);
53165             
53166             if(sortColumn != -1){
53167                 var sortDir = mstate[f];        
53168                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53169             }
53170         }
53171         
53172          
53173         
53174     },
53175
53176
53177     handleHeaderClick : function(g, index){
53178         if(this.headersDisabled){
53179             return;
53180         }
53181         var dm = g.dataSource, cm = g.colModel;
53182         if(!cm.isSortable(index)){
53183             return;
53184         }
53185         g.stopEditing();
53186         
53187         if (dm.multiSort) {
53188             // update the sortOrder
53189             var so = [];
53190             for(var i = 0; i < cm.config.length; i++ ) {
53191                 
53192                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53193                     continue; // dont' bother, it's not in sort list or being set.
53194                 }
53195                 
53196                 so.push(cm.config[i].dataIndex);
53197             };
53198             dm.sortOrder = so;
53199         }
53200         
53201         
53202         dm.sort(cm.getDataIndex(index));
53203     },
53204
53205
53206     destroy : function(){
53207         if(this.colMenu){
53208             this.colMenu.removeAll();
53209             Roo.menu.MenuMgr.unregister(this.colMenu);
53210             this.colMenu.getEl().remove();
53211             delete this.colMenu;
53212         }
53213         if(this.hmenu){
53214             this.hmenu.removeAll();
53215             Roo.menu.MenuMgr.unregister(this.hmenu);
53216             this.hmenu.getEl().remove();
53217             delete this.hmenu;
53218         }
53219         if(this.grid.enableColumnMove){
53220             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53221             if(dds){
53222                 for(var dd in dds){
53223                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53224                         var elid = dds[dd].dragElId;
53225                         dds[dd].unreg();
53226                         Roo.get(elid).remove();
53227                     } else if(dds[dd].config.isTarget){
53228                         dds[dd].proxyTop.remove();
53229                         dds[dd].proxyBottom.remove();
53230                         dds[dd].unreg();
53231                     }
53232                     if(Roo.dd.DDM.locationCache[dd]){
53233                         delete Roo.dd.DDM.locationCache[dd];
53234                     }
53235                 }
53236                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53237             }
53238         }
53239         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53240         this.bind(null, null);
53241         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53242     },
53243
53244     handleLockChange : function(){
53245         this.refresh(true);
53246     },
53247
53248     onDenyColumnLock : function(){
53249
53250     },
53251
53252     onDenyColumnHide : function(){
53253
53254     },
53255
53256     handleHdMenuClick : function(item){
53257         var index = this.hdCtxIndex;
53258         var cm = this.cm, ds = this.ds;
53259         switch(item.id){
53260             case "asc":
53261                 ds.sort(cm.getDataIndex(index), "ASC");
53262                 break;
53263             case "desc":
53264                 ds.sort(cm.getDataIndex(index), "DESC");
53265                 break;
53266             case "lock":
53267                 var lc = cm.getLockedCount();
53268                 if(cm.getColumnCount(true) <= lc+1){
53269                     this.onDenyColumnLock();
53270                     return;
53271                 }
53272                 if(lc != index){
53273                     cm.setLocked(index, true, true);
53274                     cm.moveColumn(index, lc);
53275                     this.grid.fireEvent("columnmove", index, lc);
53276                 }else{
53277                     cm.setLocked(index, true);
53278                 }
53279             break;
53280             case "unlock":
53281                 var lc = cm.getLockedCount();
53282                 if((lc-1) != index){
53283                     cm.setLocked(index, false, true);
53284                     cm.moveColumn(index, lc-1);
53285                     this.grid.fireEvent("columnmove", index, lc-1);
53286                 }else{
53287                     cm.setLocked(index, false);
53288                 }
53289             break;
53290             default:
53291                 index = cm.getIndexById(item.id.substr(4));
53292                 if(index != -1){
53293                     if(item.checked && cm.getColumnCount(true) <= 1){
53294                         this.onDenyColumnHide();
53295                         return false;
53296                     }
53297                     cm.setHidden(index, item.checked);
53298                 }
53299         }
53300         return true;
53301     },
53302
53303     beforeColMenuShow : function(){
53304         var cm = this.cm,  colCount = cm.getColumnCount();
53305         this.colMenu.removeAll();
53306         for(var i = 0; i < colCount; i++){
53307             this.colMenu.add(new Roo.menu.CheckItem({
53308                 id: "col-"+cm.getColumnId(i),
53309                 text: cm.getColumnHeader(i),
53310                 checked: !cm.isHidden(i),
53311                 hideOnClick:false
53312             }));
53313         }
53314     },
53315
53316     handleHdCtx : function(g, index, e){
53317         e.stopEvent();
53318         var hd = this.getHeaderCell(index);
53319         this.hdCtxIndex = index;
53320         var ms = this.hmenu.items, cm = this.cm;
53321         ms.get("asc").setDisabled(!cm.isSortable(index));
53322         ms.get("desc").setDisabled(!cm.isSortable(index));
53323         if(this.grid.enableColLock !== false){
53324             ms.get("lock").setDisabled(cm.isLocked(index));
53325             ms.get("unlock").setDisabled(!cm.isLocked(index));
53326         }
53327         this.hmenu.show(hd, "tl-bl");
53328     },
53329
53330     handleHdOver : function(e){
53331         var hd = this.findHeaderCell(e.getTarget());
53332         if(hd && !this.headersDisabled){
53333             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53334                this.fly(hd).addClass("x-grid-hd-over");
53335             }
53336         }
53337     },
53338
53339     handleHdOut : function(e){
53340         var hd = this.findHeaderCell(e.getTarget());
53341         if(hd){
53342             this.fly(hd).removeClass("x-grid-hd-over");
53343         }
53344     },
53345
53346     handleSplitDblClick : function(e, t){
53347         var i = this.getCellIndex(t);
53348         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53349             this.autoSizeColumn(i, true);
53350             this.layout();
53351         }
53352     },
53353
53354     render : function(){
53355
53356         var cm = this.cm;
53357         var colCount = cm.getColumnCount();
53358
53359         if(this.grid.monitorWindowResize === true){
53360             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53361         }
53362         var header = this.renderHeaders();
53363         var body = this.templates.body.apply({rows:""});
53364         var html = this.templates.master.apply({
53365             lockedBody: body,
53366             body: body,
53367             lockedHeader: header[0],
53368             header: header[1]
53369         });
53370
53371         //this.updateColumns();
53372
53373         this.grid.getGridEl().dom.innerHTML = html;
53374
53375         this.initElements();
53376         
53377         // a kludge to fix the random scolling effect in webkit
53378         this.el.on("scroll", function() {
53379             this.el.dom.scrollTop=0; // hopefully not recursive..
53380         },this);
53381
53382         this.scroller.on("scroll", this.handleScroll, this);
53383         this.lockedBody.on("mousewheel", this.handleWheel, this);
53384         this.mainBody.on("mousewheel", this.handleWheel, this);
53385
53386         this.mainHd.on("mouseover", this.handleHdOver, this);
53387         this.mainHd.on("mouseout", this.handleHdOut, this);
53388         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53389                 {delegate: "."+this.splitClass});
53390
53391         this.lockedHd.on("mouseover", this.handleHdOver, this);
53392         this.lockedHd.on("mouseout", this.handleHdOut, this);
53393         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53394                 {delegate: "."+this.splitClass});
53395
53396         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53397             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53398         }
53399
53400         this.updateSplitters();
53401
53402         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53403             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53404             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53405         }
53406
53407         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53408             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53409             this.hmenu.add(
53410                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53411                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53412             );
53413             if(this.grid.enableColLock !== false){
53414                 this.hmenu.add('-',
53415                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53416                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53417                 );
53418             }
53419             if(this.grid.enableColumnHide !== false){
53420
53421                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53422                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53423                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53424
53425                 this.hmenu.add('-',
53426                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53427                 );
53428             }
53429             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53430
53431             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53432         }
53433
53434         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53435             this.dd = new Roo.grid.GridDragZone(this.grid, {
53436                 ddGroup : this.grid.ddGroup || 'GridDD'
53437             });
53438             
53439         }
53440
53441         /*
53442         for(var i = 0; i < colCount; i++){
53443             if(cm.isHidden(i)){
53444                 this.hideColumn(i);
53445             }
53446             if(cm.config[i].align){
53447                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53448                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53449             }
53450         }*/
53451         
53452         this.updateHeaderSortState();
53453
53454         this.beforeInitialResize();
53455         this.layout(true);
53456
53457         // two part rendering gives faster view to the user
53458         this.renderPhase2.defer(1, this);
53459     },
53460
53461     renderPhase2 : function(){
53462         // render the rows now
53463         this.refresh();
53464         if(this.grid.autoSizeColumns){
53465             this.autoSizeColumns();
53466         }
53467     },
53468
53469     beforeInitialResize : function(){
53470
53471     },
53472
53473     onColumnSplitterMoved : function(i, w){
53474         this.userResized = true;
53475         var cm = this.grid.colModel;
53476         cm.setColumnWidth(i, w, true);
53477         var cid = cm.getColumnId(i);
53478         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53479         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53480         this.updateSplitters();
53481         this.layout();
53482         this.grid.fireEvent("columnresize", i, w);
53483     },
53484
53485     syncRowHeights : function(startIndex, endIndex){
53486         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53487             startIndex = startIndex || 0;
53488             var mrows = this.getBodyTable().rows;
53489             var lrows = this.getLockedTable().rows;
53490             var len = mrows.length-1;
53491             endIndex = Math.min(endIndex || len, len);
53492             for(var i = startIndex; i <= endIndex; i++){
53493                 var m = mrows[i], l = lrows[i];
53494                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53495                 m.style.height = l.style.height = h + "px";
53496             }
53497         }
53498     },
53499
53500     layout : function(initialRender, is2ndPass){
53501         var g = this.grid;
53502         var auto = g.autoHeight;
53503         var scrollOffset = 16;
53504         var c = g.getGridEl(), cm = this.cm,
53505                 expandCol = g.autoExpandColumn,
53506                 gv = this;
53507         //c.beginMeasure();
53508
53509         if(!c.dom.offsetWidth){ // display:none?
53510             if(initialRender){
53511                 this.lockedWrap.show();
53512                 this.mainWrap.show();
53513             }
53514             return;
53515         }
53516
53517         var hasLock = this.cm.isLocked(0);
53518
53519         var tbh = this.headerPanel.getHeight();
53520         var bbh = this.footerPanel.getHeight();
53521
53522         if(auto){
53523             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53524             var newHeight = ch + c.getBorderWidth("tb");
53525             if(g.maxHeight){
53526                 newHeight = Math.min(g.maxHeight, newHeight);
53527             }
53528             c.setHeight(newHeight);
53529         }
53530
53531         if(g.autoWidth){
53532             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53533         }
53534
53535         var s = this.scroller;
53536
53537         var csize = c.getSize(true);
53538
53539         this.el.setSize(csize.width, csize.height);
53540
53541         this.headerPanel.setWidth(csize.width);
53542         this.footerPanel.setWidth(csize.width);
53543
53544         var hdHeight = this.mainHd.getHeight();
53545         var vw = csize.width;
53546         var vh = csize.height - (tbh + bbh);
53547
53548         s.setSize(vw, vh);
53549
53550         var bt = this.getBodyTable();
53551         var ltWidth = hasLock ?
53552                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53553
53554         var scrollHeight = bt.offsetHeight;
53555         var scrollWidth = ltWidth + bt.offsetWidth;
53556         var vscroll = false, hscroll = false;
53557
53558         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53559
53560         var lw = this.lockedWrap, mw = this.mainWrap;
53561         var lb = this.lockedBody, mb = this.mainBody;
53562
53563         setTimeout(function(){
53564             var t = s.dom.offsetTop;
53565             var w = s.dom.clientWidth,
53566                 h = s.dom.clientHeight;
53567
53568             lw.setTop(t);
53569             lw.setSize(ltWidth, h);
53570
53571             mw.setLeftTop(ltWidth, t);
53572             mw.setSize(w-ltWidth, h);
53573
53574             lb.setHeight(h-hdHeight);
53575             mb.setHeight(h-hdHeight);
53576
53577             if(is2ndPass !== true && !gv.userResized && expandCol){
53578                 // high speed resize without full column calculation
53579                 
53580                 var ci = cm.getIndexById(expandCol);
53581                 if (ci < 0) {
53582                     ci = cm.findColumnIndex(expandCol);
53583                 }
53584                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53585                 var expandId = cm.getColumnId(ci);
53586                 var  tw = cm.getTotalWidth(false);
53587                 var currentWidth = cm.getColumnWidth(ci);
53588                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53589                 if(currentWidth != cw){
53590                     cm.setColumnWidth(ci, cw, true);
53591                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53592                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53593                     gv.updateSplitters();
53594                     gv.layout(false, true);
53595                 }
53596             }
53597
53598             if(initialRender){
53599                 lw.show();
53600                 mw.show();
53601             }
53602             //c.endMeasure();
53603         }, 10);
53604     },
53605
53606     onWindowResize : function(){
53607         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53608             return;
53609         }
53610         this.layout();
53611     },
53612
53613     appendFooter : function(parentEl){
53614         return null;
53615     },
53616
53617     sortAscText : "Sort Ascending",
53618     sortDescText : "Sort Descending",
53619     lockText : "Lock Column",
53620     unlockText : "Unlock Column",
53621     columnsText : "Columns"
53622 });
53623
53624
53625 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53626     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53627     this.proxy.el.addClass('x-grid3-col-dd');
53628 };
53629
53630 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53631     handleMouseDown : function(e){
53632
53633     },
53634
53635     callHandleMouseDown : function(e){
53636         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53637     }
53638 });
53639 /*
53640  * Based on:
53641  * Ext JS Library 1.1.1
53642  * Copyright(c) 2006-2007, Ext JS, LLC.
53643  *
53644  * Originally Released Under LGPL - original licence link has changed is not relivant.
53645  *
53646  * Fork - LGPL
53647  * <script type="text/javascript">
53648  */
53649  
53650 // private
53651 // This is a support class used internally by the Grid components
53652 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53653     this.grid = grid;
53654     this.view = grid.getView();
53655     this.proxy = this.view.resizeProxy;
53656     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53657         "gridSplitters" + this.grid.getGridEl().id, {
53658         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53659     });
53660     this.setHandleElId(Roo.id(hd));
53661     this.setOuterHandleElId(Roo.id(hd2));
53662     this.scroll = false;
53663 };
53664 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53665     fly: Roo.Element.fly,
53666
53667     b4StartDrag : function(x, y){
53668         this.view.headersDisabled = true;
53669         this.proxy.setHeight(this.view.mainWrap.getHeight());
53670         var w = this.cm.getColumnWidth(this.cellIndex);
53671         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53672         this.resetConstraints();
53673         this.setXConstraint(minw, 1000);
53674         this.setYConstraint(0, 0);
53675         this.minX = x - minw;
53676         this.maxX = x + 1000;
53677         this.startPos = x;
53678         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53679     },
53680
53681
53682     handleMouseDown : function(e){
53683         ev = Roo.EventObject.setEvent(e);
53684         var t = this.fly(ev.getTarget());
53685         if(t.hasClass("x-grid-split")){
53686             this.cellIndex = this.view.getCellIndex(t.dom);
53687             this.split = t.dom;
53688             this.cm = this.grid.colModel;
53689             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53690                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53691             }
53692         }
53693     },
53694
53695     endDrag : function(e){
53696         this.view.headersDisabled = false;
53697         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53698         var diff = endX - this.startPos;
53699         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53700     },
53701
53702     autoOffset : function(){
53703         this.setDelta(0,0);
53704     }
53705 });/*
53706  * Based on:
53707  * Ext JS Library 1.1.1
53708  * Copyright(c) 2006-2007, Ext JS, LLC.
53709  *
53710  * Originally Released Under LGPL - original licence link has changed is not relivant.
53711  *
53712  * Fork - LGPL
53713  * <script type="text/javascript">
53714  */
53715  
53716 // private
53717 // This is a support class used internally by the Grid components
53718 Roo.grid.GridDragZone = function(grid, config){
53719     this.view = grid.getView();
53720     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53721     if(this.view.lockedBody){
53722         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53723         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53724     }
53725     this.scroll = false;
53726     this.grid = grid;
53727     this.ddel = document.createElement('div');
53728     this.ddel.className = 'x-grid-dd-wrap';
53729 };
53730
53731 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53732     ddGroup : "GridDD",
53733
53734     getDragData : function(e){
53735         var t = Roo.lib.Event.getTarget(e);
53736         var rowIndex = this.view.findRowIndex(t);
53737         var sm = this.grid.selModel;
53738             
53739         //Roo.log(rowIndex);
53740         
53741         if (sm.getSelectedCell) {
53742             // cell selection..
53743             if (!sm.getSelectedCell()) {
53744                 return false;
53745             }
53746             if (rowIndex != sm.getSelectedCell()[0]) {
53747                 return false;
53748             }
53749         
53750         }
53751         
53752         if(rowIndex !== false){
53753             
53754             // if editorgrid.. 
53755             
53756             
53757             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53758                
53759             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53760               //  
53761             //}
53762             if (e.hasModifier()){
53763                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53764             }
53765             
53766             Roo.log("getDragData");
53767             
53768             return {
53769                 grid: this.grid,
53770                 ddel: this.ddel,
53771                 rowIndex: rowIndex,
53772                 selections:sm.getSelections ? sm.getSelections() : (
53773                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53774                 )
53775             };
53776         }
53777         return false;
53778     },
53779
53780     onInitDrag : function(e){
53781         var data = this.dragData;
53782         this.ddel.innerHTML = this.grid.getDragDropText();
53783         this.proxy.update(this.ddel);
53784         // fire start drag?
53785     },
53786
53787     afterRepair : function(){
53788         this.dragging = false;
53789     },
53790
53791     getRepairXY : function(e, data){
53792         return false;
53793     },
53794
53795     onEndDrag : function(data, e){
53796         // fire end drag?
53797     },
53798
53799     onValidDrop : function(dd, e, id){
53800         // fire drag drop?
53801         this.hideProxy();
53802     },
53803
53804     beforeInvalidDrop : function(e, id){
53805
53806     }
53807 });/*
53808  * Based on:
53809  * Ext JS Library 1.1.1
53810  * Copyright(c) 2006-2007, Ext JS, LLC.
53811  *
53812  * Originally Released Under LGPL - original licence link has changed is not relivant.
53813  *
53814  * Fork - LGPL
53815  * <script type="text/javascript">
53816  */
53817  
53818
53819 /**
53820  * @class Roo.grid.ColumnModel
53821  * @extends Roo.util.Observable
53822  * This is the default implementation of a ColumnModel used by the Grid. It defines
53823  * the columns in the grid.
53824  * <br>Usage:<br>
53825  <pre><code>
53826  var colModel = new Roo.grid.ColumnModel([
53827         {header: "Ticker", width: 60, sortable: true, locked: true},
53828         {header: "Company Name", width: 150, sortable: true},
53829         {header: "Market Cap.", width: 100, sortable: true},
53830         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53831         {header: "Employees", width: 100, sortable: true, resizable: false}
53832  ]);
53833  </code></pre>
53834  * <p>
53835  
53836  * The config options listed for this class are options which may appear in each
53837  * individual column definition.
53838  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53839  * @constructor
53840  * @param {Object} config An Array of column config objects. See this class's
53841  * config objects for details.
53842 */
53843 Roo.grid.ColumnModel = function(config){
53844         /**
53845      * The config passed into the constructor
53846      */
53847     this.config = config;
53848     this.lookup = {};
53849
53850     // if no id, create one
53851     // if the column does not have a dataIndex mapping,
53852     // map it to the order it is in the config
53853     for(var i = 0, len = config.length; i < len; i++){
53854         var c = config[i];
53855         if(typeof c.dataIndex == "undefined"){
53856             c.dataIndex = i;
53857         }
53858         if(typeof c.renderer == "string"){
53859             c.renderer = Roo.util.Format[c.renderer];
53860         }
53861         if(typeof c.id == "undefined"){
53862             c.id = Roo.id();
53863         }
53864         if(c.editor && c.editor.xtype){
53865             c.editor  = Roo.factory(c.editor, Roo.grid);
53866         }
53867         if(c.editor && c.editor.isFormField){
53868             c.editor = new Roo.grid.GridEditor(c.editor);
53869         }
53870         this.lookup[c.id] = c;
53871     }
53872
53873     /**
53874      * The width of columns which have no width specified (defaults to 100)
53875      * @type Number
53876      */
53877     this.defaultWidth = 100;
53878
53879     /**
53880      * Default sortable of columns which have no sortable specified (defaults to false)
53881      * @type Boolean
53882      */
53883     this.defaultSortable = false;
53884
53885     this.addEvents({
53886         /**
53887              * @event widthchange
53888              * Fires when the width of a column changes.
53889              * @param {ColumnModel} this
53890              * @param {Number} columnIndex The column index
53891              * @param {Number} newWidth The new width
53892              */
53893             "widthchange": true,
53894         /**
53895              * @event headerchange
53896              * Fires when the text of a header changes.
53897              * @param {ColumnModel} this
53898              * @param {Number} columnIndex The column index
53899              * @param {Number} newText The new header text
53900              */
53901             "headerchange": true,
53902         /**
53903              * @event hiddenchange
53904              * Fires when a column is hidden or "unhidden".
53905              * @param {ColumnModel} this
53906              * @param {Number} columnIndex The column index
53907              * @param {Boolean} hidden true if hidden, false otherwise
53908              */
53909             "hiddenchange": true,
53910             /**
53911          * @event columnmoved
53912          * Fires when a column is moved.
53913          * @param {ColumnModel} this
53914          * @param {Number} oldIndex
53915          * @param {Number} newIndex
53916          */
53917         "columnmoved" : true,
53918         /**
53919          * @event columlockchange
53920          * Fires when a column's locked state is changed
53921          * @param {ColumnModel} this
53922          * @param {Number} colIndex
53923          * @param {Boolean} locked true if locked
53924          */
53925         "columnlockchange" : true
53926     });
53927     Roo.grid.ColumnModel.superclass.constructor.call(this);
53928 };
53929 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
53930     /**
53931      * @cfg {String} header The header text to display in the Grid view.
53932      */
53933     /**
53934      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
53935      * {@link Roo.data.Record} definition from which to draw the column's value. If not
53936      * specified, the column's index is used as an index into the Record's data Array.
53937      */
53938     /**
53939      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
53940      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
53941      */
53942     /**
53943      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
53944      * Defaults to the value of the {@link #defaultSortable} property.
53945      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
53946      */
53947     /**
53948      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
53949      */
53950     /**
53951      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
53952      */
53953     /**
53954      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
53955      */
53956     /**
53957      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
53958      */
53959     /**
53960      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
53961      * given the cell's data value. See {@link #setRenderer}. If not specified, the
53962      * default renderer uses the raw data value.
53963      */
53964        /**
53965      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
53966      */
53967     /**
53968      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
53969      */
53970
53971     /**
53972      * Returns the id of the column at the specified index.
53973      * @param {Number} index The column index
53974      * @return {String} the id
53975      */
53976     getColumnId : function(index){
53977         return this.config[index].id;
53978     },
53979
53980     /**
53981      * Returns the column for a specified id.
53982      * @param {String} id The column id
53983      * @return {Object} the column
53984      */
53985     getColumnById : function(id){
53986         return this.lookup[id];
53987     },
53988
53989     
53990     /**
53991      * Returns the column for a specified dataIndex.
53992      * @param {String} dataIndex The column dataIndex
53993      * @return {Object|Boolean} the column or false if not found
53994      */
53995     getColumnByDataIndex: function(dataIndex){
53996         var index = this.findColumnIndex(dataIndex);
53997         return index > -1 ? this.config[index] : false;
53998     },
53999     
54000     /**
54001      * Returns the index for a specified column id.
54002      * @param {String} id The column id
54003      * @return {Number} the index, or -1 if not found
54004      */
54005     getIndexById : function(id){
54006         for(var i = 0, len = this.config.length; i < len; i++){
54007             if(this.config[i].id == id){
54008                 return i;
54009             }
54010         }
54011         return -1;
54012     },
54013     
54014     /**
54015      * Returns the index for a specified column dataIndex.
54016      * @param {String} dataIndex The column dataIndex
54017      * @return {Number} the index, or -1 if not found
54018      */
54019     
54020     findColumnIndex : function(dataIndex){
54021         for(var i = 0, len = this.config.length; i < len; i++){
54022             if(this.config[i].dataIndex == dataIndex){
54023                 return i;
54024             }
54025         }
54026         return -1;
54027     },
54028     
54029     
54030     moveColumn : function(oldIndex, newIndex){
54031         var c = this.config[oldIndex];
54032         this.config.splice(oldIndex, 1);
54033         this.config.splice(newIndex, 0, c);
54034         this.dataMap = null;
54035         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54036     },
54037
54038     isLocked : function(colIndex){
54039         return this.config[colIndex].locked === true;
54040     },
54041
54042     setLocked : function(colIndex, value, suppressEvent){
54043         if(this.isLocked(colIndex) == value){
54044             return;
54045         }
54046         this.config[colIndex].locked = value;
54047         if(!suppressEvent){
54048             this.fireEvent("columnlockchange", this, colIndex, value);
54049         }
54050     },
54051
54052     getTotalLockedWidth : function(){
54053         var totalWidth = 0;
54054         for(var i = 0; i < this.config.length; i++){
54055             if(this.isLocked(i) && !this.isHidden(i)){
54056                 this.totalWidth += this.getColumnWidth(i);
54057             }
54058         }
54059         return totalWidth;
54060     },
54061
54062     getLockedCount : function(){
54063         for(var i = 0, len = this.config.length; i < len; i++){
54064             if(!this.isLocked(i)){
54065                 return i;
54066             }
54067         }
54068     },
54069
54070     /**
54071      * Returns the number of columns.
54072      * @return {Number}
54073      */
54074     getColumnCount : function(visibleOnly){
54075         if(visibleOnly === true){
54076             var c = 0;
54077             for(var i = 0, len = this.config.length; i < len; i++){
54078                 if(!this.isHidden(i)){
54079                     c++;
54080                 }
54081             }
54082             return c;
54083         }
54084         return this.config.length;
54085     },
54086
54087     /**
54088      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54089      * @param {Function} fn
54090      * @param {Object} scope (optional)
54091      * @return {Array} result
54092      */
54093     getColumnsBy : function(fn, scope){
54094         var r = [];
54095         for(var i = 0, len = this.config.length; i < len; i++){
54096             var c = this.config[i];
54097             if(fn.call(scope||this, c, i) === true){
54098                 r[r.length] = c;
54099             }
54100         }
54101         return r;
54102     },
54103
54104     /**
54105      * Returns true if the specified column is sortable.
54106      * @param {Number} col The column index
54107      * @return {Boolean}
54108      */
54109     isSortable : function(col){
54110         if(typeof this.config[col].sortable == "undefined"){
54111             return this.defaultSortable;
54112         }
54113         return this.config[col].sortable;
54114     },
54115
54116     /**
54117      * Returns the rendering (formatting) function defined for the column.
54118      * @param {Number} col The column index.
54119      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54120      */
54121     getRenderer : function(col){
54122         if(!this.config[col].renderer){
54123             return Roo.grid.ColumnModel.defaultRenderer;
54124         }
54125         return this.config[col].renderer;
54126     },
54127
54128     /**
54129      * Sets the rendering (formatting) function for a column.
54130      * @param {Number} col The column index
54131      * @param {Function} fn The function to use to process the cell's raw data
54132      * to return HTML markup for the grid view. The render function is called with
54133      * the following parameters:<ul>
54134      * <li>Data value.</li>
54135      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54136      * <li>css A CSS style string to apply to the table cell.</li>
54137      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54138      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54139      * <li>Row index</li>
54140      * <li>Column index</li>
54141      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54142      */
54143     setRenderer : function(col, fn){
54144         this.config[col].renderer = fn;
54145     },
54146
54147     /**
54148      * Returns the width for the specified column.
54149      * @param {Number} col The column index
54150      * @return {Number}
54151      */
54152     getColumnWidth : function(col){
54153         return this.config[col].width * 1 || this.defaultWidth;
54154     },
54155
54156     /**
54157      * Sets the width for a column.
54158      * @param {Number} col The column index
54159      * @param {Number} width The new width
54160      */
54161     setColumnWidth : function(col, width, suppressEvent){
54162         this.config[col].width = width;
54163         this.totalWidth = null;
54164         if(!suppressEvent){
54165              this.fireEvent("widthchange", this, col, width);
54166         }
54167     },
54168
54169     /**
54170      * Returns the total width of all columns.
54171      * @param {Boolean} includeHidden True to include hidden column widths
54172      * @return {Number}
54173      */
54174     getTotalWidth : function(includeHidden){
54175         if(!this.totalWidth){
54176             this.totalWidth = 0;
54177             for(var i = 0, len = this.config.length; i < len; i++){
54178                 if(includeHidden || !this.isHidden(i)){
54179                     this.totalWidth += this.getColumnWidth(i);
54180                 }
54181             }
54182         }
54183         return this.totalWidth;
54184     },
54185
54186     /**
54187      * Returns the header for the specified column.
54188      * @param {Number} col The column index
54189      * @return {String}
54190      */
54191     getColumnHeader : function(col){
54192         return this.config[col].header;
54193     },
54194
54195     /**
54196      * Sets the header for a column.
54197      * @param {Number} col The column index
54198      * @param {String} header The new header
54199      */
54200     setColumnHeader : function(col, header){
54201         this.config[col].header = header;
54202         this.fireEvent("headerchange", this, col, header);
54203     },
54204
54205     /**
54206      * Returns the tooltip for the specified column.
54207      * @param {Number} col The column index
54208      * @return {String}
54209      */
54210     getColumnTooltip : function(col){
54211             return this.config[col].tooltip;
54212     },
54213     /**
54214      * Sets the tooltip for a column.
54215      * @param {Number} col The column index
54216      * @param {String} tooltip The new tooltip
54217      */
54218     setColumnTooltip : function(col, tooltip){
54219             this.config[col].tooltip = tooltip;
54220     },
54221
54222     /**
54223      * Returns the dataIndex for the specified column.
54224      * @param {Number} col The column index
54225      * @return {Number}
54226      */
54227     getDataIndex : function(col){
54228         return this.config[col].dataIndex;
54229     },
54230
54231     /**
54232      * Sets the dataIndex for a column.
54233      * @param {Number} col The column index
54234      * @param {Number} dataIndex The new dataIndex
54235      */
54236     setDataIndex : function(col, dataIndex){
54237         this.config[col].dataIndex = dataIndex;
54238     },
54239
54240     
54241     
54242     /**
54243      * Returns true if the cell is editable.
54244      * @param {Number} colIndex The column index
54245      * @param {Number} rowIndex The row index
54246      * @return {Boolean}
54247      */
54248     isCellEditable : function(colIndex, rowIndex){
54249         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54250     },
54251
54252     /**
54253      * Returns the editor defined for the cell/column.
54254      * return false or null to disable editing.
54255      * @param {Number} colIndex The column index
54256      * @param {Number} rowIndex The row index
54257      * @return {Object}
54258      */
54259     getCellEditor : function(colIndex, rowIndex){
54260         return this.config[colIndex].editor;
54261     },
54262
54263     /**
54264      * Sets if a column is editable.
54265      * @param {Number} col The column index
54266      * @param {Boolean} editable True if the column is editable
54267      */
54268     setEditable : function(col, editable){
54269         this.config[col].editable = editable;
54270     },
54271
54272
54273     /**
54274      * Returns true if the column is hidden.
54275      * @param {Number} colIndex The column index
54276      * @return {Boolean}
54277      */
54278     isHidden : function(colIndex){
54279         return this.config[colIndex].hidden;
54280     },
54281
54282
54283     /**
54284      * Returns true if the column width cannot be changed
54285      */
54286     isFixed : function(colIndex){
54287         return this.config[colIndex].fixed;
54288     },
54289
54290     /**
54291      * Returns true if the column can be resized
54292      * @return {Boolean}
54293      */
54294     isResizable : function(colIndex){
54295         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54296     },
54297     /**
54298      * Sets if a column is hidden.
54299      * @param {Number} colIndex The column index
54300      * @param {Boolean} hidden True if the column is hidden
54301      */
54302     setHidden : function(colIndex, hidden){
54303         this.config[colIndex].hidden = hidden;
54304         this.totalWidth = null;
54305         this.fireEvent("hiddenchange", this, colIndex, hidden);
54306     },
54307
54308     /**
54309      * Sets the editor for a column.
54310      * @param {Number} col The column index
54311      * @param {Object} editor The editor object
54312      */
54313     setEditor : function(col, editor){
54314         this.config[col].editor = editor;
54315     }
54316 });
54317
54318 Roo.grid.ColumnModel.defaultRenderer = function(value){
54319         if(typeof value == "string" && value.length < 1){
54320             return "&#160;";
54321         }
54322         return value;
54323 };
54324
54325 // Alias for backwards compatibility
54326 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54327 /*
54328  * Based on:
54329  * Ext JS Library 1.1.1
54330  * Copyright(c) 2006-2007, Ext JS, LLC.
54331  *
54332  * Originally Released Under LGPL - original licence link has changed is not relivant.
54333  *
54334  * Fork - LGPL
54335  * <script type="text/javascript">
54336  */
54337
54338 /**
54339  * @class Roo.grid.AbstractSelectionModel
54340  * @extends Roo.util.Observable
54341  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54342  * implemented by descendant classes.  This class should not be directly instantiated.
54343  * @constructor
54344  */
54345 Roo.grid.AbstractSelectionModel = function(){
54346     this.locked = false;
54347     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54348 };
54349
54350 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54351     /** @ignore Called by the grid automatically. Do not call directly. */
54352     init : function(grid){
54353         this.grid = grid;
54354         this.initEvents();
54355     },
54356
54357     /**
54358      * Locks the selections.
54359      */
54360     lock : function(){
54361         this.locked = true;
54362     },
54363
54364     /**
54365      * Unlocks the selections.
54366      */
54367     unlock : function(){
54368         this.locked = false;
54369     },
54370
54371     /**
54372      * Returns true if the selections are locked.
54373      * @return {Boolean}
54374      */
54375     isLocked : function(){
54376         return this.locked;
54377     }
54378 });/*
54379  * Based on:
54380  * Ext JS Library 1.1.1
54381  * Copyright(c) 2006-2007, Ext JS, LLC.
54382  *
54383  * Originally Released Under LGPL - original licence link has changed is not relivant.
54384  *
54385  * Fork - LGPL
54386  * <script type="text/javascript">
54387  */
54388 /**
54389  * @extends Roo.grid.AbstractSelectionModel
54390  * @class Roo.grid.RowSelectionModel
54391  * The default SelectionModel used by {@link Roo.grid.Grid}.
54392  * It supports multiple selections and keyboard selection/navigation. 
54393  * @constructor
54394  * @param {Object} config
54395  */
54396 Roo.grid.RowSelectionModel = function(config){
54397     Roo.apply(this, config);
54398     this.selections = new Roo.util.MixedCollection(false, function(o){
54399         return o.id;
54400     });
54401
54402     this.last = false;
54403     this.lastActive = false;
54404
54405     this.addEvents({
54406         /**
54407              * @event selectionchange
54408              * Fires when the selection changes
54409              * @param {SelectionModel} this
54410              */
54411             "selectionchange" : true,
54412         /**
54413              * @event afterselectionchange
54414              * Fires after the selection changes (eg. by key press or clicking)
54415              * @param {SelectionModel} this
54416              */
54417             "afterselectionchange" : true,
54418         /**
54419              * @event beforerowselect
54420              * Fires when a row is selected being selected, return false to cancel.
54421              * @param {SelectionModel} this
54422              * @param {Number} rowIndex The selected index
54423              * @param {Boolean} keepExisting False if other selections will be cleared
54424              */
54425             "beforerowselect" : true,
54426         /**
54427              * @event rowselect
54428              * Fires when a row is selected.
54429              * @param {SelectionModel} this
54430              * @param {Number} rowIndex The selected index
54431              * @param {Roo.data.Record} r The record
54432              */
54433             "rowselect" : true,
54434         /**
54435              * @event rowdeselect
54436              * Fires when a row is deselected.
54437              * @param {SelectionModel} this
54438              * @param {Number} rowIndex The selected index
54439              */
54440         "rowdeselect" : true
54441     });
54442     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54443     this.locked = false;
54444 };
54445
54446 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54447     /**
54448      * @cfg {Boolean} singleSelect
54449      * True to allow selection of only one row at a time (defaults to false)
54450      */
54451     singleSelect : false,
54452
54453     // private
54454     initEvents : function(){
54455
54456         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54457             this.grid.on("mousedown", this.handleMouseDown, this);
54458         }else{ // allow click to work like normal
54459             this.grid.on("rowclick", this.handleDragableRowClick, this);
54460         }
54461
54462         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54463             "up" : function(e){
54464                 if(!e.shiftKey){
54465                     this.selectPrevious(e.shiftKey);
54466                 }else if(this.last !== false && this.lastActive !== false){
54467                     var last = this.last;
54468                     this.selectRange(this.last,  this.lastActive-1);
54469                     this.grid.getView().focusRow(this.lastActive);
54470                     if(last !== false){
54471                         this.last = last;
54472                     }
54473                 }else{
54474                     this.selectFirstRow();
54475                 }
54476                 this.fireEvent("afterselectionchange", this);
54477             },
54478             "down" : function(e){
54479                 if(!e.shiftKey){
54480                     this.selectNext(e.shiftKey);
54481                 }else if(this.last !== false && this.lastActive !== false){
54482                     var last = this.last;
54483                     this.selectRange(this.last,  this.lastActive+1);
54484                     this.grid.getView().focusRow(this.lastActive);
54485                     if(last !== false){
54486                         this.last = last;
54487                     }
54488                 }else{
54489                     this.selectFirstRow();
54490                 }
54491                 this.fireEvent("afterselectionchange", this);
54492             },
54493             scope: this
54494         });
54495
54496         var view = this.grid.view;
54497         view.on("refresh", this.onRefresh, this);
54498         view.on("rowupdated", this.onRowUpdated, this);
54499         view.on("rowremoved", this.onRemove, this);
54500     },
54501
54502     // private
54503     onRefresh : function(){
54504         var ds = this.grid.dataSource, i, v = this.grid.view;
54505         var s = this.selections;
54506         s.each(function(r){
54507             if((i = ds.indexOfId(r.id)) != -1){
54508                 v.onRowSelect(i);
54509             }else{
54510                 s.remove(r);
54511             }
54512         });
54513     },
54514
54515     // private
54516     onRemove : function(v, index, r){
54517         this.selections.remove(r);
54518     },
54519
54520     // private
54521     onRowUpdated : function(v, index, r){
54522         if(this.isSelected(r)){
54523             v.onRowSelect(index);
54524         }
54525     },
54526
54527     /**
54528      * Select records.
54529      * @param {Array} records The records to select
54530      * @param {Boolean} keepExisting (optional) True to keep existing selections
54531      */
54532     selectRecords : function(records, keepExisting){
54533         if(!keepExisting){
54534             this.clearSelections();
54535         }
54536         var ds = this.grid.dataSource;
54537         for(var i = 0, len = records.length; i < len; i++){
54538             this.selectRow(ds.indexOf(records[i]), true);
54539         }
54540     },
54541
54542     /**
54543      * Gets the number of selected rows.
54544      * @return {Number}
54545      */
54546     getCount : function(){
54547         return this.selections.length;
54548     },
54549
54550     /**
54551      * Selects the first row in the grid.
54552      */
54553     selectFirstRow : function(){
54554         this.selectRow(0);
54555     },
54556
54557     /**
54558      * Select the last row.
54559      * @param {Boolean} keepExisting (optional) True to keep existing selections
54560      */
54561     selectLastRow : function(keepExisting){
54562         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54563     },
54564
54565     /**
54566      * Selects the row immediately following the last selected row.
54567      * @param {Boolean} keepExisting (optional) True to keep existing selections
54568      */
54569     selectNext : function(keepExisting){
54570         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54571             this.selectRow(this.last+1, keepExisting);
54572             this.grid.getView().focusRow(this.last);
54573         }
54574     },
54575
54576     /**
54577      * Selects the row that precedes the last selected row.
54578      * @param {Boolean} keepExisting (optional) True to keep existing selections
54579      */
54580     selectPrevious : function(keepExisting){
54581         if(this.last){
54582             this.selectRow(this.last-1, keepExisting);
54583             this.grid.getView().focusRow(this.last);
54584         }
54585     },
54586
54587     /**
54588      * Returns the selected records
54589      * @return {Array} Array of selected records
54590      */
54591     getSelections : function(){
54592         return [].concat(this.selections.items);
54593     },
54594
54595     /**
54596      * Returns the first selected record.
54597      * @return {Record}
54598      */
54599     getSelected : function(){
54600         return this.selections.itemAt(0);
54601     },
54602
54603
54604     /**
54605      * Clears all selections.
54606      */
54607     clearSelections : function(fast){
54608         if(this.locked) return;
54609         if(fast !== true){
54610             var ds = this.grid.dataSource;
54611             var s = this.selections;
54612             s.each(function(r){
54613                 this.deselectRow(ds.indexOfId(r.id));
54614             }, this);
54615             s.clear();
54616         }else{
54617             this.selections.clear();
54618         }
54619         this.last = false;
54620     },
54621
54622
54623     /**
54624      * Selects all rows.
54625      */
54626     selectAll : function(){
54627         if(this.locked) return;
54628         this.selections.clear();
54629         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54630             this.selectRow(i, true);
54631         }
54632     },
54633
54634     /**
54635      * Returns True if there is a selection.
54636      * @return {Boolean}
54637      */
54638     hasSelection : function(){
54639         return this.selections.length > 0;
54640     },
54641
54642     /**
54643      * Returns True if the specified row is selected.
54644      * @param {Number/Record} record The record or index of the record to check
54645      * @return {Boolean}
54646      */
54647     isSelected : function(index){
54648         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54649         return (r && this.selections.key(r.id) ? true : false);
54650     },
54651
54652     /**
54653      * Returns True if the specified record id is selected.
54654      * @param {String} id The id of record to check
54655      * @return {Boolean}
54656      */
54657     isIdSelected : function(id){
54658         return (this.selections.key(id) ? true : false);
54659     },
54660
54661     // private
54662     handleMouseDown : function(e, t){
54663         var view = this.grid.getView(), rowIndex;
54664         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54665             return;
54666         };
54667         if(e.shiftKey && this.last !== false){
54668             var last = this.last;
54669             this.selectRange(last, rowIndex, e.ctrlKey);
54670             this.last = last; // reset the last
54671             view.focusRow(rowIndex);
54672         }else{
54673             var isSelected = this.isSelected(rowIndex);
54674             if(e.button !== 0 && isSelected){
54675                 view.focusRow(rowIndex);
54676             }else if(e.ctrlKey && isSelected){
54677                 this.deselectRow(rowIndex);
54678             }else if(!isSelected){
54679                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54680                 view.focusRow(rowIndex);
54681             }
54682         }
54683         this.fireEvent("afterselectionchange", this);
54684     },
54685     // private
54686     handleDragableRowClick :  function(grid, rowIndex, e) 
54687     {
54688         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54689             this.selectRow(rowIndex, false);
54690             grid.view.focusRow(rowIndex);
54691              this.fireEvent("afterselectionchange", this);
54692         }
54693     },
54694     
54695     /**
54696      * Selects multiple rows.
54697      * @param {Array} rows Array of the indexes of the row to select
54698      * @param {Boolean} keepExisting (optional) True to keep existing selections
54699      */
54700     selectRows : function(rows, keepExisting){
54701         if(!keepExisting){
54702             this.clearSelections();
54703         }
54704         for(var i = 0, len = rows.length; i < len; i++){
54705             this.selectRow(rows[i], true);
54706         }
54707     },
54708
54709     /**
54710      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54711      * @param {Number} startRow The index of the first row in the range
54712      * @param {Number} endRow The index of the last row in the range
54713      * @param {Boolean} keepExisting (optional) True to retain existing selections
54714      */
54715     selectRange : function(startRow, endRow, keepExisting){
54716         if(this.locked) return;
54717         if(!keepExisting){
54718             this.clearSelections();
54719         }
54720         if(startRow <= endRow){
54721             for(var i = startRow; i <= endRow; i++){
54722                 this.selectRow(i, true);
54723             }
54724         }else{
54725             for(var i = startRow; i >= endRow; i--){
54726                 this.selectRow(i, true);
54727             }
54728         }
54729     },
54730
54731     /**
54732      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54733      * @param {Number} startRow The index of the first row in the range
54734      * @param {Number} endRow The index of the last row in the range
54735      */
54736     deselectRange : function(startRow, endRow, preventViewNotify){
54737         if(this.locked) return;
54738         for(var i = startRow; i <= endRow; i++){
54739             this.deselectRow(i, preventViewNotify);
54740         }
54741     },
54742
54743     /**
54744      * Selects a row.
54745      * @param {Number} row The index of the row to select
54746      * @param {Boolean} keepExisting (optional) True to keep existing selections
54747      */
54748     selectRow : function(index, keepExisting, preventViewNotify){
54749         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54750         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54751             if(!keepExisting || this.singleSelect){
54752                 this.clearSelections();
54753             }
54754             var r = this.grid.dataSource.getAt(index);
54755             this.selections.add(r);
54756             this.last = this.lastActive = index;
54757             if(!preventViewNotify){
54758                 this.grid.getView().onRowSelect(index);
54759             }
54760             this.fireEvent("rowselect", this, index, r);
54761             this.fireEvent("selectionchange", this);
54762         }
54763     },
54764
54765     /**
54766      * Deselects a row.
54767      * @param {Number} row The index of the row to deselect
54768      */
54769     deselectRow : function(index, preventViewNotify){
54770         if(this.locked) return;
54771         if(this.last == index){
54772             this.last = false;
54773         }
54774         if(this.lastActive == index){
54775             this.lastActive = false;
54776         }
54777         var r = this.grid.dataSource.getAt(index);
54778         this.selections.remove(r);
54779         if(!preventViewNotify){
54780             this.grid.getView().onRowDeselect(index);
54781         }
54782         this.fireEvent("rowdeselect", this, index);
54783         this.fireEvent("selectionchange", this);
54784     },
54785
54786     // private
54787     restoreLast : function(){
54788         if(this._last){
54789             this.last = this._last;
54790         }
54791     },
54792
54793     // private
54794     acceptsNav : function(row, col, cm){
54795         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54796     },
54797
54798     // private
54799     onEditorKey : function(field, e){
54800         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54801         if(k == e.TAB){
54802             e.stopEvent();
54803             ed.completeEdit();
54804             if(e.shiftKey){
54805                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54806             }else{
54807                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54808             }
54809         }else if(k == e.ENTER && !e.ctrlKey){
54810             e.stopEvent();
54811             ed.completeEdit();
54812             if(e.shiftKey){
54813                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54814             }else{
54815                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54816             }
54817         }else if(k == e.ESC){
54818             ed.cancelEdit();
54819         }
54820         if(newCell){
54821             g.startEditing(newCell[0], newCell[1]);
54822         }
54823     }
54824 });/*
54825  * Based on:
54826  * Ext JS Library 1.1.1
54827  * Copyright(c) 2006-2007, Ext JS, LLC.
54828  *
54829  * Originally Released Under LGPL - original licence link has changed is not relivant.
54830  *
54831  * Fork - LGPL
54832  * <script type="text/javascript">
54833  */
54834 /**
54835  * @class Roo.grid.CellSelectionModel
54836  * @extends Roo.grid.AbstractSelectionModel
54837  * This class provides the basic implementation for cell selection in a grid.
54838  * @constructor
54839  * @param {Object} config The object containing the configuration of this model.
54840  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54841  */
54842 Roo.grid.CellSelectionModel = function(config){
54843     Roo.apply(this, config);
54844
54845     this.selection = null;
54846
54847     this.addEvents({
54848         /**
54849              * @event beforerowselect
54850              * Fires before a cell is selected.
54851              * @param {SelectionModel} this
54852              * @param {Number} rowIndex The selected row index
54853              * @param {Number} colIndex The selected cell index
54854              */
54855             "beforecellselect" : true,
54856         /**
54857              * @event cellselect
54858              * Fires when a cell is selected.
54859              * @param {SelectionModel} this
54860              * @param {Number} rowIndex The selected row index
54861              * @param {Number} colIndex The selected cell index
54862              */
54863             "cellselect" : true,
54864         /**
54865              * @event selectionchange
54866              * Fires when the active selection changes.
54867              * @param {SelectionModel} this
54868              * @param {Object} selection null for no selection or an object (o) with two properties
54869                 <ul>
54870                 <li>o.record: the record object for the row the selection is in</li>
54871                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54872                 </ul>
54873              */
54874             "selectionchange" : true,
54875         /**
54876              * @event tabend
54877              * Fires when the tab (or enter) was pressed on the last editable cell
54878              * You can use this to trigger add new row.
54879              * @param {SelectionModel} this
54880              */
54881             "tabend" : true,
54882          /**
54883              * @event beforeeditnext
54884              * Fires before the next editable sell is made active
54885              * You can use this to skip to another cell or fire the tabend
54886              *    if you set cell to false
54887              * @param {Object} eventdata object : { cell : [ row, col ] } 
54888              */
54889             "beforeeditnext" : true
54890     });
54891     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54892 };
54893
54894 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54895     
54896     enter_is_tab: false,
54897
54898     /** @ignore */
54899     initEvents : function(){
54900         this.grid.on("mousedown", this.handleMouseDown, this);
54901         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54902         var view = this.grid.view;
54903         view.on("refresh", this.onViewChange, this);
54904         view.on("rowupdated", this.onRowUpdated, this);
54905         view.on("beforerowremoved", this.clearSelections, this);
54906         view.on("beforerowsinserted", this.clearSelections, this);
54907         if(this.grid.isEditor){
54908             this.grid.on("beforeedit", this.beforeEdit,  this);
54909         }
54910     },
54911
54912         //private
54913     beforeEdit : function(e){
54914         this.select(e.row, e.column, false, true, e.record);
54915     },
54916
54917         //private
54918     onRowUpdated : function(v, index, r){
54919         if(this.selection && this.selection.record == r){
54920             v.onCellSelect(index, this.selection.cell[1]);
54921         }
54922     },
54923
54924         //private
54925     onViewChange : function(){
54926         this.clearSelections(true);
54927     },
54928
54929         /**
54930          * Returns the currently selected cell,.
54931          * @return {Array} The selected cell (row, column) or null if none selected.
54932          */
54933     getSelectedCell : function(){
54934         return this.selection ? this.selection.cell : null;
54935     },
54936
54937     /**
54938      * Clears all selections.
54939      * @param {Boolean} true to prevent the gridview from being notified about the change.
54940      */
54941     clearSelections : function(preventNotify){
54942         var s = this.selection;
54943         if(s){
54944             if(preventNotify !== true){
54945                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
54946             }
54947             this.selection = null;
54948             this.fireEvent("selectionchange", this, null);
54949         }
54950     },
54951
54952     /**
54953      * Returns true if there is a selection.
54954      * @return {Boolean}
54955      */
54956     hasSelection : function(){
54957         return this.selection ? true : false;
54958     },
54959
54960     /** @ignore */
54961     handleMouseDown : function(e, t){
54962         var v = this.grid.getView();
54963         if(this.isLocked()){
54964             return;
54965         };
54966         var row = v.findRowIndex(t);
54967         var cell = v.findCellIndex(t);
54968         if(row !== false && cell !== false){
54969             this.select(row, cell);
54970         }
54971     },
54972
54973     /**
54974      * Selects a cell.
54975      * @param {Number} rowIndex
54976      * @param {Number} collIndex
54977      */
54978     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
54979         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
54980             this.clearSelections();
54981             r = r || this.grid.dataSource.getAt(rowIndex);
54982             this.selection = {
54983                 record : r,
54984                 cell : [rowIndex, colIndex]
54985             };
54986             if(!preventViewNotify){
54987                 var v = this.grid.getView();
54988                 v.onCellSelect(rowIndex, colIndex);
54989                 if(preventFocus !== true){
54990                     v.focusCell(rowIndex, colIndex);
54991                 }
54992             }
54993             this.fireEvent("cellselect", this, rowIndex, colIndex);
54994             this.fireEvent("selectionchange", this, this.selection);
54995         }
54996     },
54997
54998         //private
54999     isSelectable : function(rowIndex, colIndex, cm){
55000         return !cm.isHidden(colIndex);
55001     },
55002
55003     /** @ignore */
55004     handleKeyDown : function(e){
55005         //Roo.log('Cell Sel Model handleKeyDown');
55006         if(!e.isNavKeyPress()){
55007             return;
55008         }
55009         var g = this.grid, s = this.selection;
55010         if(!s){
55011             e.stopEvent();
55012             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55013             if(cell){
55014                 this.select(cell[0], cell[1]);
55015             }
55016             return;
55017         }
55018         var sm = this;
55019         var walk = function(row, col, step){
55020             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55021         };
55022         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55023         var newCell;
55024
55025       
55026
55027         switch(k){
55028             case e.TAB:
55029                 // handled by onEditorKey
55030                 if (g.isEditor && g.editing) {
55031                     return;
55032                 }
55033                 if(e.shiftKey) {
55034                     newCell = walk(r, c-1, -1);
55035                 } else {
55036                     newCell = walk(r, c+1, 1);
55037                 }
55038                 break;
55039             
55040             case e.DOWN:
55041                newCell = walk(r+1, c, 1);
55042                 break;
55043             
55044             case e.UP:
55045                 newCell = walk(r-1, c, -1);
55046                 break;
55047             
55048             case e.RIGHT:
55049                 newCell = walk(r, c+1, 1);
55050                 break;
55051             
55052             case e.LEFT:
55053                 newCell = walk(r, c-1, -1);
55054                 break;
55055             
55056             case e.ENTER:
55057                 
55058                 if(g.isEditor && !g.editing){
55059                    g.startEditing(r, c);
55060                    e.stopEvent();
55061                    return;
55062                 }
55063                 
55064                 
55065              break;
55066         };
55067         if(newCell){
55068             this.select(newCell[0], newCell[1]);
55069             e.stopEvent();
55070             
55071         }
55072     },
55073
55074     acceptsNav : function(row, col, cm){
55075         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55076     },
55077     /**
55078      * Selects a cell.
55079      * @param {Number} field (not used) - as it's normally used as a listener
55080      * @param {Number} e - event - fake it by using
55081      *
55082      * var e = Roo.EventObjectImpl.prototype;
55083      * e.keyCode = e.TAB
55084      *
55085      * 
55086      */
55087     onEditorKey : function(field, e){
55088         
55089         var k = e.getKey(),
55090             newCell,
55091             g = this.grid,
55092             ed = g.activeEditor,
55093             forward = false;
55094         ///Roo.log('onEditorKey' + k);
55095         
55096         
55097         if (this.enter_is_tab && k == e.ENTER) {
55098             k = e.TAB;
55099         }
55100         
55101         if(k == e.TAB){
55102             if(e.shiftKey){
55103                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55104             }else{
55105                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55106                 forward = true;
55107             }
55108             
55109             e.stopEvent();
55110             
55111         } else if(k == e.ENTER &&  !e.ctrlKey){
55112             ed.completeEdit();
55113             e.stopEvent();
55114             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55115         
55116                 } else if(k == e.ESC){
55117             ed.cancelEdit();
55118         }
55119                 
55120         if (newCell) {
55121             var ecall = { cell : newCell, forward : forward };
55122             this.fireEvent('beforeeditnext', ecall );
55123             newCell = ecall.cell;
55124                         forward = ecall.forward;
55125         }
55126                 
55127         if(newCell){
55128             //Roo.log('next cell after edit');
55129             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55130         } else if (forward) {
55131             // tabbed past last
55132             this.fireEvent.defer(100, this, ['tabend',this]);
55133         }
55134     }
55135 });/*
55136  * Based on:
55137  * Ext JS Library 1.1.1
55138  * Copyright(c) 2006-2007, Ext JS, LLC.
55139  *
55140  * Originally Released Under LGPL - original licence link has changed is not relivant.
55141  *
55142  * Fork - LGPL
55143  * <script type="text/javascript">
55144  */
55145  
55146 /**
55147  * @class Roo.grid.EditorGrid
55148  * @extends Roo.grid.Grid
55149  * Class for creating and editable grid.
55150  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55151  * The container MUST have some type of size defined for the grid to fill. The container will be 
55152  * automatically set to position relative if it isn't already.
55153  * @param {Object} dataSource The data model to bind to
55154  * @param {Object} colModel The column model with info about this grid's columns
55155  */
55156 Roo.grid.EditorGrid = function(container, config){
55157     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55158     this.getGridEl().addClass("xedit-grid");
55159
55160     if(!this.selModel){
55161         this.selModel = new Roo.grid.CellSelectionModel();
55162     }
55163
55164     this.activeEditor = null;
55165
55166         this.addEvents({
55167             /**
55168              * @event beforeedit
55169              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55170              * <ul style="padding:5px;padding-left:16px;">
55171              * <li>grid - This grid</li>
55172              * <li>record - The record being edited</li>
55173              * <li>field - The field name being edited</li>
55174              * <li>value - The value for the field being edited.</li>
55175              * <li>row - The grid row index</li>
55176              * <li>column - The grid column index</li>
55177              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55178              * </ul>
55179              * @param {Object} e An edit event (see above for description)
55180              */
55181             "beforeedit" : true,
55182             /**
55183              * @event afteredit
55184              * Fires after a cell is edited. <br />
55185              * <ul style="padding:5px;padding-left:16px;">
55186              * <li>grid - This grid</li>
55187              * <li>record - The record being edited</li>
55188              * <li>field - The field name being edited</li>
55189              * <li>value - The value being set</li>
55190              * <li>originalValue - The original value for the field, before the edit.</li>
55191              * <li>row - The grid row index</li>
55192              * <li>column - The grid column index</li>
55193              * </ul>
55194              * @param {Object} e An edit event (see above for description)
55195              */
55196             "afteredit" : true,
55197             /**
55198              * @event validateedit
55199              * Fires after a cell is edited, but before the value is set in the record. 
55200          * You can use this to modify the value being set in the field, Return false
55201              * to cancel the change. The edit event object has the following properties <br />
55202              * <ul style="padding:5px;padding-left:16px;">
55203          * <li>editor - This editor</li>
55204              * <li>grid - This grid</li>
55205              * <li>record - The record being edited</li>
55206              * <li>field - The field name being edited</li>
55207              * <li>value - The value being set</li>
55208              * <li>originalValue - The original value for the field, before the edit.</li>
55209              * <li>row - The grid row index</li>
55210              * <li>column - The grid column index</li>
55211              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55212              * </ul>
55213              * @param {Object} e An edit event (see above for description)
55214              */
55215             "validateedit" : true
55216         });
55217     this.on("bodyscroll", this.stopEditing,  this);
55218     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55219 };
55220
55221 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55222     /**
55223      * @cfg {Number} clicksToEdit
55224      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55225      */
55226     clicksToEdit: 2,
55227
55228     // private
55229     isEditor : true,
55230     // private
55231     trackMouseOver: false, // causes very odd FF errors
55232
55233     onCellDblClick : function(g, row, col){
55234         this.startEditing(row, col);
55235     },
55236
55237     onEditComplete : function(ed, value, startValue){
55238         this.editing = false;
55239         this.activeEditor = null;
55240         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55241         var r = ed.record;
55242         var field = this.colModel.getDataIndex(ed.col);
55243         var e = {
55244             grid: this,
55245             record: r,
55246             field: field,
55247             originalValue: startValue,
55248             value: value,
55249             row: ed.row,
55250             column: ed.col,
55251             cancel:false,
55252             editor: ed
55253         };
55254         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55255         cell.show();
55256           
55257         if(String(value) !== String(startValue)){
55258             
55259             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55260                 r.set(field, e.value);
55261                 // if we are dealing with a combo box..
55262                 // then we also set the 'name' colum to be the displayField
55263                 if (ed.field.displayField && ed.field.name) {
55264                     r.set(ed.field.name, ed.field.el.dom.value);
55265                 }
55266                 
55267                 delete e.cancel; //?? why!!!
55268                 this.fireEvent("afteredit", e);
55269             }
55270         } else {
55271             this.fireEvent("afteredit", e); // always fire it!
55272         }
55273         this.view.focusCell(ed.row, ed.col);
55274     },
55275
55276     /**
55277      * Starts editing the specified for the specified row/column
55278      * @param {Number} rowIndex
55279      * @param {Number} colIndex
55280      */
55281     startEditing : function(row, col){
55282         this.stopEditing();
55283         if(this.colModel.isCellEditable(col, row)){
55284             this.view.ensureVisible(row, col, true);
55285           
55286             var r = this.dataSource.getAt(row);
55287             var field = this.colModel.getDataIndex(col);
55288             var cell = Roo.get(this.view.getCell(row,col));
55289             var e = {
55290                 grid: this,
55291                 record: r,
55292                 field: field,
55293                 value: r.data[field],
55294                 row: row,
55295                 column: col,
55296                 cancel:false 
55297             };
55298             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55299                 this.editing = true;
55300                 var ed = this.colModel.getCellEditor(col, row);
55301                 
55302                 if (!ed) {
55303                     return;
55304                 }
55305                 if(!ed.rendered){
55306                     ed.render(ed.parentEl || document.body);
55307                 }
55308                 ed.field.reset();
55309                
55310                 cell.hide();
55311                 
55312                 (function(){ // complex but required for focus issues in safari, ie and opera
55313                     ed.row = row;
55314                     ed.col = col;
55315                     ed.record = r;
55316                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55317                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55318                     this.activeEditor = ed;
55319                     var v = r.data[field];
55320                     ed.startEdit(this.view.getCell(row, col), v);
55321                     // combo's with 'displayField and name set
55322                     if (ed.field.displayField && ed.field.name) {
55323                         ed.field.el.dom.value = r.data[ed.field.name];
55324                     }
55325                     
55326                     
55327                 }).defer(50, this);
55328             }
55329         }
55330     },
55331         
55332     /**
55333      * Stops any active editing
55334      */
55335     stopEditing : function(){
55336         if(this.activeEditor){
55337             this.activeEditor.completeEdit();
55338         }
55339         this.activeEditor = null;
55340     },
55341         
55342          /**
55343      * Called to get grid's drag proxy text, by default returns this.ddText.
55344      * @return {String}
55345      */
55346     getDragDropText : function(){
55347         var count = this.selModel.getSelectedCell() ? 1 : 0;
55348         return String.format(this.ddText, count, count == 1 ? '' : 's');
55349     }
55350         
55351 });/*
55352  * Based on:
55353  * Ext JS Library 1.1.1
55354  * Copyright(c) 2006-2007, Ext JS, LLC.
55355  *
55356  * Originally Released Under LGPL - original licence link has changed is not relivant.
55357  *
55358  * Fork - LGPL
55359  * <script type="text/javascript">
55360  */
55361
55362 // private - not really -- you end up using it !
55363 // This is a support class used internally by the Grid components
55364
55365 /**
55366  * @class Roo.grid.GridEditor
55367  * @extends Roo.Editor
55368  * Class for creating and editable grid elements.
55369  * @param {Object} config any settings (must include field)
55370  */
55371 Roo.grid.GridEditor = function(field, config){
55372     if (!config && field.field) {
55373         config = field;
55374         field = Roo.factory(config.field, Roo.form);
55375     }
55376     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55377     field.monitorTab = false;
55378 };
55379
55380 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55381     
55382     /**
55383      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55384      */
55385     
55386     alignment: "tl-tl",
55387     autoSize: "width",
55388     hideEl : false,
55389     cls: "x-small-editor x-grid-editor",
55390     shim:false,
55391     shadow:"frame"
55392 });/*
55393  * Based on:
55394  * Ext JS Library 1.1.1
55395  * Copyright(c) 2006-2007, Ext JS, LLC.
55396  *
55397  * Originally Released Under LGPL - original licence link has changed is not relivant.
55398  *
55399  * Fork - LGPL
55400  * <script type="text/javascript">
55401  */
55402   
55403
55404   
55405 Roo.grid.PropertyRecord = Roo.data.Record.create([
55406     {name:'name',type:'string'},  'value'
55407 ]);
55408
55409
55410 Roo.grid.PropertyStore = function(grid, source){
55411     this.grid = grid;
55412     this.store = new Roo.data.Store({
55413         recordType : Roo.grid.PropertyRecord
55414     });
55415     this.store.on('update', this.onUpdate,  this);
55416     if(source){
55417         this.setSource(source);
55418     }
55419     Roo.grid.PropertyStore.superclass.constructor.call(this);
55420 };
55421
55422
55423
55424 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55425     setSource : function(o){
55426         this.source = o;
55427         this.store.removeAll();
55428         var data = [];
55429         for(var k in o){
55430             if(this.isEditableValue(o[k])){
55431                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55432             }
55433         }
55434         this.store.loadRecords({records: data}, {}, true);
55435     },
55436
55437     onUpdate : function(ds, record, type){
55438         if(type == Roo.data.Record.EDIT){
55439             var v = record.data['value'];
55440             var oldValue = record.modified['value'];
55441             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55442                 this.source[record.id] = v;
55443                 record.commit();
55444                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55445             }else{
55446                 record.reject();
55447             }
55448         }
55449     },
55450
55451     getProperty : function(row){
55452        return this.store.getAt(row);
55453     },
55454
55455     isEditableValue: function(val){
55456         if(val && val instanceof Date){
55457             return true;
55458         }else if(typeof val == 'object' || typeof val == 'function'){
55459             return false;
55460         }
55461         return true;
55462     },
55463
55464     setValue : function(prop, value){
55465         this.source[prop] = value;
55466         this.store.getById(prop).set('value', value);
55467     },
55468
55469     getSource : function(){
55470         return this.source;
55471     }
55472 });
55473
55474 Roo.grid.PropertyColumnModel = function(grid, store){
55475     this.grid = grid;
55476     var g = Roo.grid;
55477     g.PropertyColumnModel.superclass.constructor.call(this, [
55478         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55479         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55480     ]);
55481     this.store = store;
55482     this.bselect = Roo.DomHelper.append(document.body, {
55483         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55484             {tag: 'option', value: 'true', html: 'true'},
55485             {tag: 'option', value: 'false', html: 'false'}
55486         ]
55487     });
55488     Roo.id(this.bselect);
55489     var f = Roo.form;
55490     this.editors = {
55491         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55492         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55493         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55494         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55495         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55496     };
55497     this.renderCellDelegate = this.renderCell.createDelegate(this);
55498     this.renderPropDelegate = this.renderProp.createDelegate(this);
55499 };
55500
55501 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55502     
55503     
55504     nameText : 'Name',
55505     valueText : 'Value',
55506     
55507     dateFormat : 'm/j/Y',
55508     
55509     
55510     renderDate : function(dateVal){
55511         return dateVal.dateFormat(this.dateFormat);
55512     },
55513
55514     renderBool : function(bVal){
55515         return bVal ? 'true' : 'false';
55516     },
55517
55518     isCellEditable : function(colIndex, rowIndex){
55519         return colIndex == 1;
55520     },
55521
55522     getRenderer : function(col){
55523         return col == 1 ?
55524             this.renderCellDelegate : this.renderPropDelegate;
55525     },
55526
55527     renderProp : function(v){
55528         return this.getPropertyName(v);
55529     },
55530
55531     renderCell : function(val){
55532         var rv = val;
55533         if(val instanceof Date){
55534             rv = this.renderDate(val);
55535         }else if(typeof val == 'boolean'){
55536             rv = this.renderBool(val);
55537         }
55538         return Roo.util.Format.htmlEncode(rv);
55539     },
55540
55541     getPropertyName : function(name){
55542         var pn = this.grid.propertyNames;
55543         return pn && pn[name] ? pn[name] : name;
55544     },
55545
55546     getCellEditor : function(colIndex, rowIndex){
55547         var p = this.store.getProperty(rowIndex);
55548         var n = p.data['name'], val = p.data['value'];
55549         
55550         if(typeof(this.grid.customEditors[n]) == 'string'){
55551             return this.editors[this.grid.customEditors[n]];
55552         }
55553         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55554             return this.grid.customEditors[n];
55555         }
55556         if(val instanceof Date){
55557             return this.editors['date'];
55558         }else if(typeof val == 'number'){
55559             return this.editors['number'];
55560         }else if(typeof val == 'boolean'){
55561             return this.editors['boolean'];
55562         }else{
55563             return this.editors['string'];
55564         }
55565     }
55566 });
55567
55568 /**
55569  * @class Roo.grid.PropertyGrid
55570  * @extends Roo.grid.EditorGrid
55571  * This class represents the  interface of a component based property grid control.
55572  * <br><br>Usage:<pre><code>
55573  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55574       
55575  });
55576  // set any options
55577  grid.render();
55578  * </code></pre>
55579   
55580  * @constructor
55581  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55582  * The container MUST have some type of size defined for the grid to fill. The container will be
55583  * automatically set to position relative if it isn't already.
55584  * @param {Object} config A config object that sets properties on this grid.
55585  */
55586 Roo.grid.PropertyGrid = function(container, config){
55587     config = config || {};
55588     var store = new Roo.grid.PropertyStore(this);
55589     this.store = store;
55590     var cm = new Roo.grid.PropertyColumnModel(this, store);
55591     store.store.sort('name', 'ASC');
55592     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55593         ds: store.store,
55594         cm: cm,
55595         enableColLock:false,
55596         enableColumnMove:false,
55597         stripeRows:false,
55598         trackMouseOver: false,
55599         clicksToEdit:1
55600     }, config));
55601     this.getGridEl().addClass('x-props-grid');
55602     this.lastEditRow = null;
55603     this.on('columnresize', this.onColumnResize, this);
55604     this.addEvents({
55605          /**
55606              * @event beforepropertychange
55607              * Fires before a property changes (return false to stop?)
55608              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55609              * @param {String} id Record Id
55610              * @param {String} newval New Value
55611          * @param {String} oldval Old Value
55612              */
55613         "beforepropertychange": true,
55614         /**
55615              * @event propertychange
55616              * Fires after a property changes
55617              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55618              * @param {String} id Record Id
55619              * @param {String} newval New Value
55620          * @param {String} oldval Old Value
55621              */
55622         "propertychange": true
55623     });
55624     this.customEditors = this.customEditors || {};
55625 };
55626 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55627     
55628      /**
55629      * @cfg {Object} customEditors map of colnames=> custom editors.
55630      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55631      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55632      * false disables editing of the field.
55633          */
55634     
55635       /**
55636      * @cfg {Object} propertyNames map of property Names to their displayed value
55637          */
55638     
55639     render : function(){
55640         Roo.grid.PropertyGrid.superclass.render.call(this);
55641         this.autoSize.defer(100, this);
55642     },
55643
55644     autoSize : function(){
55645         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55646         if(this.view){
55647             this.view.fitColumns();
55648         }
55649     },
55650
55651     onColumnResize : function(){
55652         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55653         this.autoSize();
55654     },
55655     /**
55656      * Sets the data for the Grid
55657      * accepts a Key => Value object of all the elements avaiable.
55658      * @param {Object} data  to appear in grid.
55659      */
55660     setSource : function(source){
55661         this.store.setSource(source);
55662         //this.autoSize();
55663     },
55664     /**
55665      * Gets all the data from the grid.
55666      * @return {Object} data  data stored in grid
55667      */
55668     getSource : function(){
55669         return this.store.getSource();
55670     }
55671 });/*
55672  * Based on:
55673  * Ext JS Library 1.1.1
55674  * Copyright(c) 2006-2007, Ext JS, LLC.
55675  *
55676  * Originally Released Under LGPL - original licence link has changed is not relivant.
55677  *
55678  * Fork - LGPL
55679  * <script type="text/javascript">
55680  */
55681  
55682 /**
55683  * @class Roo.LoadMask
55684  * A simple utility class for generically masking elements while loading data.  If the element being masked has
55685  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
55686  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
55687  * element's UpdateManager load indicator and will be destroyed after the initial load.
55688  * @constructor
55689  * Create a new LoadMask
55690  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
55691  * @param {Object} config The config object
55692  */
55693 Roo.LoadMask = function(el, config){
55694     this.el = Roo.get(el);
55695     Roo.apply(this, config);
55696     if(this.store){
55697         this.store.on('beforeload', this.onBeforeLoad, this);
55698         this.store.on('load', this.onLoad, this);
55699         this.store.on('loadexception', this.onLoadException, this);
55700         this.removeMask = false;
55701     }else{
55702         var um = this.el.getUpdateManager();
55703         um.showLoadIndicator = false; // disable the default indicator
55704         um.on('beforeupdate', this.onBeforeLoad, this);
55705         um.on('update', this.onLoad, this);
55706         um.on('failure', this.onLoad, this);
55707         this.removeMask = true;
55708     }
55709 };
55710
55711 Roo.LoadMask.prototype = {
55712     /**
55713      * @cfg {Boolean} removeMask
55714      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
55715      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
55716      */
55717     /**
55718      * @cfg {String} msg
55719      * The text to display in a centered loading message box (defaults to 'Loading...')
55720      */
55721     msg : 'Loading...',
55722     /**
55723      * @cfg {String} msgCls
55724      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
55725      */
55726     msgCls : 'x-mask-loading',
55727
55728     /**
55729      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
55730      * @type Boolean
55731      */
55732     disabled: false,
55733
55734     /**
55735      * Disables the mask to prevent it from being displayed
55736      */
55737     disable : function(){
55738        this.disabled = true;
55739     },
55740
55741     /**
55742      * Enables the mask so that it can be displayed
55743      */
55744     enable : function(){
55745         this.disabled = false;
55746     },
55747     
55748     onLoadException : function()
55749     {
55750         Roo.log(arguments);
55751         
55752         if (typeof(arguments[3]) != 'undefined') {
55753             Roo.MessageBox.alert("Error loading",arguments[3]);
55754         } 
55755         /*
55756         try {
55757             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55758                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55759             }   
55760         } catch(e) {
55761             
55762         }
55763         */
55764     
55765         
55766         
55767         this.el.unmask(this.removeMask);
55768     },
55769     // private
55770     onLoad : function()
55771     {
55772         this.el.unmask(this.removeMask);
55773     },
55774
55775     // private
55776     onBeforeLoad : function(){
55777         if(!this.disabled){
55778             this.el.mask(this.msg, this.msgCls);
55779         }
55780     },
55781
55782     // private
55783     destroy : function(){
55784         if(this.store){
55785             this.store.un('beforeload', this.onBeforeLoad, this);
55786             this.store.un('load', this.onLoad, this);
55787             this.store.un('loadexception', this.onLoadException, this);
55788         }else{
55789             var um = this.el.getUpdateManager();
55790             um.un('beforeupdate', this.onBeforeLoad, this);
55791             um.un('update', this.onLoad, this);
55792             um.un('failure', this.onLoad, this);
55793         }
55794     }
55795 };/*
55796  * Based on:
55797  * Ext JS Library 1.1.1
55798  * Copyright(c) 2006-2007, Ext JS, LLC.
55799  *
55800  * Originally Released Under LGPL - original licence link has changed is not relivant.
55801  *
55802  * Fork - LGPL
55803  * <script type="text/javascript">
55804  */
55805
55806
55807 /**
55808  * @class Roo.XTemplate
55809  * @extends Roo.Template
55810  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
55811 <pre><code>
55812 var t = new Roo.XTemplate(
55813         '&lt;select name="{name}"&gt;',
55814                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
55815         '&lt;/select&gt;'
55816 );
55817  
55818 // then append, applying the master template values
55819  </code></pre>
55820  *
55821  * Supported features:
55822  *
55823  *  Tags:
55824
55825 <pre><code>
55826       {a_variable} - output encoded.
55827       {a_variable.format:("Y-m-d")} - call a method on the variable
55828       {a_variable:raw} - unencoded output
55829       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
55830       {a_variable:this.method_on_template(...)} - call a method on the template object.
55831  
55832 </code></pre>
55833  *  The tpl tag:
55834 <pre><code>
55835         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
55836         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
55837         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
55838         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
55839   
55840         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
55841         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
55842 </code></pre>
55843  *      
55844  */
55845 Roo.XTemplate = function()
55846 {
55847     Roo.XTemplate.superclass.constructor.apply(this, arguments);
55848     if (this.html) {
55849         this.compile();
55850     }
55851 };
55852
55853
55854 Roo.extend(Roo.XTemplate, Roo.Template, {
55855
55856     /**
55857      * The various sub templates
55858      */
55859     tpls : false,
55860     /**
55861      *
55862      * basic tag replacing syntax
55863      * WORD:WORD()
55864      *
55865      * // you can fake an object call by doing this
55866      *  x.t:(test,tesT) 
55867      * 
55868      */
55869     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
55870
55871     /**
55872      * compile the template
55873      *
55874      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
55875      *
55876      */
55877     compile: function()
55878     {
55879         var s = this.html;
55880      
55881         s = ['<tpl>', s, '</tpl>'].join('');
55882     
55883         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
55884             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
55885             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
55886             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
55887             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
55888             m,
55889             id     = 0,
55890             tpls   = [];
55891     
55892         while(true == !!(m = s.match(re))){
55893             var forMatch   = m[0].match(nameRe),
55894                 ifMatch   = m[0].match(ifRe),
55895                 execMatch   = m[0].match(execRe),
55896                 namedMatch   = m[0].match(namedRe),
55897                 
55898                 exp  = null, 
55899                 fn   = null,
55900                 exec = null,
55901                 name = forMatch && forMatch[1] ? forMatch[1] : '';
55902                 
55903             if (ifMatch) {
55904                 // if - puts fn into test..
55905                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
55906                 if(exp){
55907                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
55908                 }
55909             }
55910             
55911             if (execMatch) {
55912                 // exec - calls a function... returns empty if true is  returned.
55913                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
55914                 if(exp){
55915                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
55916                 }
55917             }
55918             
55919             
55920             if (name) {
55921                 // for = 
55922                 switch(name){
55923                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
55924                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
55925                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
55926                 }
55927             }
55928             var uid = namedMatch ? namedMatch[1] : id;
55929             
55930             
55931             tpls.push({
55932                 id:     namedMatch ? namedMatch[1] : id,
55933                 target: name,
55934                 exec:   exec,
55935                 test:   fn,
55936                 body:   m[1] || ''
55937             });
55938             if (namedMatch) {
55939                 s = s.replace(m[0], '');
55940             } else { 
55941                 s = s.replace(m[0], '{xtpl'+ id + '}');
55942             }
55943             ++id;
55944         }
55945         this.tpls = [];
55946         for(var i = tpls.length-1; i >= 0; --i){
55947             this.compileTpl(tpls[i]);
55948             this.tpls[tpls[i].id] = tpls[i];
55949         }
55950         this.master = tpls[tpls.length-1];
55951         return this;
55952     },
55953     /**
55954      * same as applyTemplate, except it's done to one of the subTemplates
55955      * when using named templates, you can do:
55956      *
55957      * var str = pl.applySubTemplate('your-name', values);
55958      *
55959      * 
55960      * @param {Number} id of the template
55961      * @param {Object} values to apply to template
55962      * @param {Object} parent (normaly the instance of this object)
55963      */
55964     applySubTemplate : function(id, values, parent)
55965     {
55966         
55967         
55968         var t = this.tpls[id];
55969         
55970         
55971         try { 
55972             if(t.test && !t.test.call(this, values, parent)){
55973                 return '';
55974             }
55975         } catch(e) {
55976             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
55977             Roo.log(e.toString());
55978             Roo.log(t.test);
55979             return ''
55980         }
55981         try { 
55982             
55983             if(t.exec && t.exec.call(this, values, parent)){
55984                 return '';
55985             }
55986         } catch(e) {
55987             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
55988             Roo.log(e.toString());
55989             Roo.log(t.exec);
55990             return ''
55991         }
55992         try {
55993             var vs = t.target ? t.target.call(this, values, parent) : values;
55994             parent = t.target ? values : parent;
55995             if(t.target && vs instanceof Array){
55996                 var buf = [];
55997                 for(var i = 0, len = vs.length; i < len; i++){
55998                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
55999                 }
56000                 return buf.join('');
56001             }
56002             return t.compiled.call(this, vs, parent);
56003         } catch (e) {
56004             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
56005             Roo.log(e.toString());
56006             Roo.log(t.compiled);
56007             return '';
56008         }
56009     },
56010
56011     compileTpl : function(tpl)
56012     {
56013         var fm = Roo.util.Format;
56014         var useF = this.disableFormats !== true;
56015         var sep = Roo.isGecko ? "+" : ",";
56016         var undef = function(str) {
56017             Roo.log("Property not found :"  + str);
56018             return '';
56019         };
56020         
56021         var fn = function(m, name, format, args)
56022         {
56023             //Roo.log(arguments);
56024             args = args ? args.replace(/\\'/g,"'") : args;
56025             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
56026             if (typeof(format) == 'undefined') {
56027                 format= 'htmlEncode';
56028             }
56029             if (format == 'raw' ) {
56030                 format = false;
56031             }
56032             
56033             if(name.substr(0, 4) == 'xtpl'){
56034                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
56035             }
56036             
56037             // build an array of options to determine if value is undefined..
56038             
56039             // basically get 'xxxx.yyyy' then do
56040             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
56041             //    (function () { Roo.log("Property not found"); return ''; })() :
56042             //    ......
56043             
56044             var udef_ar = [];
56045             var lookfor = '';
56046             Roo.each(name.split('.'), function(st) {
56047                 lookfor += (lookfor.length ? '.': '') + st;
56048                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
56049             });
56050             
56051             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
56052             
56053             
56054             if(format && useF){
56055                 
56056                 args = args ? ',' + args : "";
56057                  
56058                 if(format.substr(0, 5) != "this."){
56059                     format = "fm." + format + '(';
56060                 }else{
56061                     format = 'this.call("'+ format.substr(5) + '", ';
56062                     args = ", values";
56063                 }
56064                 
56065                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
56066             }
56067              
56068             if (args.length) {
56069                 // called with xxyx.yuu:(test,test)
56070                 // change to ()
56071                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
56072             }
56073             // raw.. - :raw modifier..
56074             return "'"+ sep + udef_st  + name + ")"+sep+"'";
56075             
56076         };
56077         var body;
56078         // branched to use + in gecko and [].join() in others
56079         if(Roo.isGecko){
56080             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
56081                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
56082                     "';};};";
56083         }else{
56084             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
56085             body.push(tpl.body.replace(/(\r\n|\n)/g,
56086                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
56087             body.push("'].join('');};};");
56088             body = body.join('');
56089         }
56090         
56091         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
56092        
56093         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
56094         eval(body);
56095         
56096         return this;
56097     },
56098
56099     applyTemplate : function(values){
56100         return this.master.compiled.call(this, values, {});
56101         //var s = this.subs;
56102     },
56103
56104     apply : function(){
56105         return this.applyTemplate.apply(this, arguments);
56106     }
56107
56108  });
56109
56110 Roo.XTemplate.from = function(el){
56111     el = Roo.getDom(el);
56112     return new Roo.XTemplate(el.value || el.innerHTML);
56113 };