roojs-all.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|Object} name The attribute name (or object to set multiple attributes)
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 (typeof(name) == 'object') {
9582                 for(var i in name) {
9583                     this.attr(i, name[i]);
9584                 }
9585                 return name;
9586             }
9587             
9588             
9589             if (!this.dom.hasAttribute(name)) {
9590                 return undefined;
9591             }
9592             return this.dom.getAttribute(name);
9593         }
9594         
9595         
9596         
9597     };
9598
9599     var ep = El.prototype;
9600
9601     /**
9602      * Appends an event handler (Shorthand for addListener)
9603      * @param {String}   eventName     The type of event to append
9604      * @param {Function} fn        The method the event invokes
9605      * @param {Object} scope       (optional) The scope (this object) of the fn
9606      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9607      * @method
9608      */
9609     ep.on = ep.addListener;
9610         // backwards compat
9611     ep.mon = ep.addListener;
9612
9613     /**
9614      * Removes an event handler from this element (shorthand for removeListener)
9615      * @param {String} eventName the type of event to remove
9616      * @param {Function} fn the method the event invokes
9617      * @return {Roo.Element} this
9618      * @method
9619      */
9620     ep.un = ep.removeListener;
9621
9622     /**
9623      * true to automatically adjust width and height settings for box-model issues (default to true)
9624      */
9625     ep.autoBoxAdjust = true;
9626
9627     // private
9628     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9629
9630     // private
9631     El.addUnits = function(v, defaultUnit){
9632         if(v === "" || v == "auto"){
9633             return v;
9634         }
9635         if(v === undefined){
9636             return '';
9637         }
9638         if(typeof v == "number" || !El.unitPattern.test(v)){
9639             return v + (defaultUnit || 'px');
9640         }
9641         return v;
9642     };
9643
9644     // special markup used throughout Roo when box wrapping elements
9645     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>';
9646     /**
9647      * Visibility mode constant - Use visibility to hide element
9648      * @static
9649      * @type Number
9650      */
9651     El.VISIBILITY = 1;
9652     /**
9653      * Visibility mode constant - Use display to hide element
9654      * @static
9655      * @type Number
9656      */
9657     El.DISPLAY = 2;
9658
9659     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9660     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9661     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9662
9663
9664
9665     /**
9666      * @private
9667      */
9668     El.cache = {};
9669
9670     var docEl;
9671
9672     /**
9673      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9674      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9675      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9676      * @return {Element} The Element object
9677      * @static
9678      */
9679     El.get = function(el){
9680         var ex, elm, id;
9681         if(!el){ return null; }
9682         if(typeof el == "string"){ // element id
9683             if(!(elm = document.getElementById(el))){
9684                 return null;
9685             }
9686             if(ex = El.cache[el]){
9687                 ex.dom = elm;
9688             }else{
9689                 ex = El.cache[el] = new El(elm);
9690             }
9691             return ex;
9692         }else if(el.tagName){ // dom element
9693             if(!(id = el.id)){
9694                 id = Roo.id(el);
9695             }
9696             if(ex = El.cache[id]){
9697                 ex.dom = el;
9698             }else{
9699                 ex = El.cache[id] = new El(el);
9700             }
9701             return ex;
9702         }else if(el instanceof El){
9703             if(el != docEl){
9704                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9705                                                               // catch case where it hasn't been appended
9706                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9707             }
9708             return el;
9709         }else if(el.isComposite){
9710             return el;
9711         }else if(el instanceof Array){
9712             return El.select(el);
9713         }else if(el == document){
9714             // create a bogus element object representing the document object
9715             if(!docEl){
9716                 var f = function(){};
9717                 f.prototype = El.prototype;
9718                 docEl = new f();
9719                 docEl.dom = document;
9720             }
9721             return docEl;
9722         }
9723         return null;
9724     };
9725
9726     // private
9727     El.uncache = function(el){
9728         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9729             if(a[i]){
9730                 delete El.cache[a[i].id || a[i]];
9731             }
9732         }
9733     };
9734
9735     // private
9736     // Garbage collection - uncache elements/purge listeners on orphaned elements
9737     // so we don't hold a reference and cause the browser to retain them
9738     El.garbageCollect = function(){
9739         if(!Roo.enableGarbageCollector){
9740             clearInterval(El.collectorThread);
9741             return;
9742         }
9743         for(var eid in El.cache){
9744             var el = El.cache[eid], d = el.dom;
9745             // -------------------------------------------------------
9746             // Determining what is garbage:
9747             // -------------------------------------------------------
9748             // !d
9749             // dom node is null, definitely garbage
9750             // -------------------------------------------------------
9751             // !d.parentNode
9752             // no parentNode == direct orphan, definitely garbage
9753             // -------------------------------------------------------
9754             // !d.offsetParent && !document.getElementById(eid)
9755             // display none elements have no offsetParent so we will
9756             // also try to look it up by it's id. However, check
9757             // offsetParent first so we don't do unneeded lookups.
9758             // This enables collection of elements that are not orphans
9759             // directly, but somewhere up the line they have an orphan
9760             // parent.
9761             // -------------------------------------------------------
9762             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9763                 delete El.cache[eid];
9764                 if(d && Roo.enableListenerCollection){
9765                     E.purgeElement(d);
9766                 }
9767             }
9768         }
9769     }
9770     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9771
9772
9773     // dom is optional
9774     El.Flyweight = function(dom){
9775         this.dom = dom;
9776     };
9777     El.Flyweight.prototype = El.prototype;
9778
9779     El._flyweights = {};
9780     /**
9781      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9782      * the dom node can be overwritten by other code.
9783      * @param {String/HTMLElement} el The dom node or id
9784      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9785      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9786      * @static
9787      * @return {Element} The shared Element object
9788      */
9789     El.fly = function(el, named){
9790         named = named || '_global';
9791         el = Roo.getDom(el);
9792         if(!el){
9793             return null;
9794         }
9795         if(!El._flyweights[named]){
9796             El._flyweights[named] = new El.Flyweight();
9797         }
9798         El._flyweights[named].dom = el;
9799         return El._flyweights[named];
9800     };
9801
9802     /**
9803      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9804      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9805      * Shorthand of {@link Roo.Element#get}
9806      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9807      * @return {Element} The Element object
9808      * @member Roo
9809      * @method get
9810      */
9811     Roo.get = El.get;
9812     /**
9813      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9814      * the dom node can be overwritten by other code.
9815      * Shorthand of {@link Roo.Element#fly}
9816      * @param {String/HTMLElement} el The dom node or id
9817      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9818      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9819      * @static
9820      * @return {Element} The shared Element object
9821      * @member Roo
9822      * @method fly
9823      */
9824     Roo.fly = El.fly;
9825
9826     // speedy lookup for elements never to box adjust
9827     var noBoxAdjust = Roo.isStrict ? {
9828         select:1
9829     } : {
9830         input:1, select:1, textarea:1
9831     };
9832     if(Roo.isIE || Roo.isGecko){
9833         noBoxAdjust['button'] = 1;
9834     }
9835
9836
9837     Roo.EventManager.on(window, 'unload', function(){
9838         delete El.cache;
9839         delete El._flyweights;
9840     });
9841 })();
9842
9843
9844
9845
9846 if(Roo.DomQuery){
9847     Roo.Element.selectorFunction = Roo.DomQuery.select;
9848 }
9849
9850 Roo.Element.select = function(selector, unique, root){
9851     var els;
9852     if(typeof selector == "string"){
9853         els = Roo.Element.selectorFunction(selector, root);
9854     }else if(selector.length !== undefined){
9855         els = selector;
9856     }else{
9857         throw "Invalid selector";
9858     }
9859     if(unique === true){
9860         return new Roo.CompositeElement(els);
9861     }else{
9862         return new Roo.CompositeElementLite(els);
9863     }
9864 };
9865 /**
9866  * Selects elements based on the passed CSS selector to enable working on them as 1.
9867  * @param {String/Array} selector The CSS selector or an array of elements
9868  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9869  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9870  * @return {CompositeElementLite/CompositeElement}
9871  * @member Roo
9872  * @method select
9873  */
9874 Roo.select = Roo.Element.select;
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889 /*
9890  * Based on:
9891  * Ext JS Library 1.1.1
9892  * Copyright(c) 2006-2007, Ext JS, LLC.
9893  *
9894  * Originally Released Under LGPL - original licence link has changed is not relivant.
9895  *
9896  * Fork - LGPL
9897  * <script type="text/javascript">
9898  */
9899
9900
9901
9902 //Notifies Element that fx methods are available
9903 Roo.enableFx = true;
9904
9905 /**
9906  * @class Roo.Fx
9907  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9908  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9909  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9910  * Element effects to work.</p><br/>
9911  *
9912  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9913  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9914  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9915  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9916  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9917  * expected results and should be done with care.</p><br/>
9918  *
9919  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9920  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9921 <pre>
9922 Value  Description
9923 -----  -----------------------------
9924 tl     The top left corner
9925 t      The center of the top edge
9926 tr     The top right corner
9927 l      The center of the left edge
9928 r      The center of the right edge
9929 bl     The bottom left corner
9930 b      The center of the bottom edge
9931 br     The bottom right corner
9932 </pre>
9933  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9934  * below are common options that can be passed to any Fx method.</b>
9935  * @cfg {Function} callback A function called when the effect is finished
9936  * @cfg {Object} scope The scope of the effect function
9937  * @cfg {String} easing A valid Easing value for the effect
9938  * @cfg {String} afterCls A css class to apply after the effect
9939  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9940  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9941  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9942  * effects that end with the element being visually hidden, ignored otherwise)
9943  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9944  * a function which returns such a specification that will be applied to the Element after the effect finishes
9945  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9946  * @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
9947  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9948  */
9949 Roo.Fx = {
9950         /**
9951          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9952          * origin for the slide effect.  This function automatically handles wrapping the element with
9953          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9954          * Usage:
9955          *<pre><code>
9956 // default: slide the element in from the top
9957 el.slideIn();
9958
9959 // custom: slide the element in from the right with a 2-second duration
9960 el.slideIn('r', { duration: 2 });
9961
9962 // common config options shown with default values
9963 el.slideIn('t', {
9964     easing: 'easeOut',
9965     duration: .5
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideIn : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // fix display to visibility
9981             this.fixDisplay();
9982
9983             // restore values after effect
9984             var r = this.getFxRestore();
9985             var b = this.getBox();
9986             // fixed size for slide
9987             this.setSize(b);
9988
9989             // wrap if needed
9990             var wrap = this.fxWrap(r.pos, o, "hidden");
9991
9992             var st = this.dom.style;
9993             st.visibility = "visible";
9994             st.position = "absolute";
9995
9996             // clear out temp styles after slide and unwrap
9997             var after = function(){
9998                 el.fxUnwrap(wrap, r.pos, o);
9999                 st.width = r.width;
10000                 st.height = r.height;
10001                 el.afterFx(o);
10002             };
10003             // time to calc the positions
10004             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10005
10006             switch(anchor.toLowerCase()){
10007                 case "t":
10008                     wrap.setSize(b.width, 0);
10009                     st.left = st.bottom = "0";
10010                     a = {height: bh};
10011                 break;
10012                 case "l":
10013                     wrap.setSize(0, b.height);
10014                     st.right = st.top = "0";
10015                     a = {width: bw};
10016                 break;
10017                 case "r":
10018                     wrap.setSize(0, b.height);
10019                     wrap.setX(b.right);
10020                     st.left = st.top = "0";
10021                     a = {width: bw, points: pt};
10022                 break;
10023                 case "b":
10024                     wrap.setSize(b.width, 0);
10025                     wrap.setY(b.bottom);
10026                     st.left = st.top = "0";
10027                     a = {height: bh, points: pt};
10028                 break;
10029                 case "tl":
10030                     wrap.setSize(0, 0);
10031                     st.right = st.bottom = "0";
10032                     a = {width: bw, height: bh};
10033                 break;
10034                 case "bl":
10035                     wrap.setSize(0, 0);
10036                     wrap.setY(b.y+b.height);
10037                     st.right = st.top = "0";
10038                     a = {width: bw, height: bh, points: pt};
10039                 break;
10040                 case "br":
10041                     wrap.setSize(0, 0);
10042                     wrap.setXY([b.right, b.bottom]);
10043                     st.left = st.top = "0";
10044                     a = {width: bw, height: bh, points: pt};
10045                 break;
10046                 case "tr":
10047                     wrap.setSize(0, 0);
10048                     wrap.setX(b.x+b.width);
10049                     st.left = st.bottom = "0";
10050                     a = {width: bw, height: bh, points: pt};
10051                 break;
10052             }
10053             this.dom.style.visibility = "visible";
10054             wrap.show();
10055
10056             arguments.callee.anim = wrap.fxanim(a,
10057                 o,
10058                 'motion',
10059                 .5,
10060                 'easeOut', after);
10061         });
10062         return this;
10063     },
10064     
10065         /**
10066          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10067          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10068          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10069          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10070          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10071          * Usage:
10072          *<pre><code>
10073 // default: slide the element out to the top
10074 el.slideOut();
10075
10076 // custom: slide the element out to the right with a 2-second duration
10077 el.slideOut('r', { duration: 2 });
10078
10079 // common config options shown with default values
10080 el.slideOut('t', {
10081     easing: 'easeOut',
10082     duration: .5,
10083     remove: false,
10084     useDisplay: false
10085 });
10086 </code></pre>
10087          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10088          * @param {Object} options (optional) Object literal with any of the Fx config options
10089          * @return {Roo.Element} The Element
10090          */
10091     slideOut : function(anchor, o){
10092         var el = this.getFxEl();
10093         o = o || {};
10094
10095         el.queueFx(o, function(){
10096
10097             anchor = anchor || "t";
10098
10099             // restore values after effect
10100             var r = this.getFxRestore();
10101             
10102             var b = this.getBox();
10103             // fixed size for slide
10104             this.setSize(b);
10105
10106             // wrap if needed
10107             var wrap = this.fxWrap(r.pos, o, "visible");
10108
10109             var st = this.dom.style;
10110             st.visibility = "visible";
10111             st.position = "absolute";
10112
10113             wrap.setSize(b);
10114
10115             var after = function(){
10116                 if(o.useDisplay){
10117                     el.setDisplayed(false);
10118                 }else{
10119                     el.hide();
10120                 }
10121
10122                 el.fxUnwrap(wrap, r.pos, o);
10123
10124                 st.width = r.width;
10125                 st.height = r.height;
10126
10127                 el.afterFx(o);
10128             };
10129
10130             var a, zero = {to: 0};
10131             switch(anchor.toLowerCase()){
10132                 case "t":
10133                     st.left = st.bottom = "0";
10134                     a = {height: zero};
10135                 break;
10136                 case "l":
10137                     st.right = st.top = "0";
10138                     a = {width: zero};
10139                 break;
10140                 case "r":
10141                     st.left = st.top = "0";
10142                     a = {width: zero, points: {to:[b.right, b.y]}};
10143                 break;
10144                 case "b":
10145                     st.left = st.top = "0";
10146                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "tl":
10149                     st.right = st.bottom = "0";
10150                     a = {width: zero, height: zero};
10151                 break;
10152                 case "bl":
10153                     st.right = st.top = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10155                 break;
10156                 case "br":
10157                     st.left = st.top = "0";
10158                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10159                 break;
10160                 case "tr":
10161                     st.left = st.bottom = "0";
10162                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10163                 break;
10164             }
10165
10166             arguments.callee.anim = wrap.fxanim(a,
10167                 o,
10168                 'motion',
10169                 .5,
10170                 "easeOut", after);
10171         });
10172         return this;
10173     },
10174
10175         /**
10176          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10177          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10178          * The element must be removed from the DOM using the 'remove' config option if desired.
10179          * Usage:
10180          *<pre><code>
10181 // default
10182 el.puff();
10183
10184 // common config options shown with default values
10185 el.puff({
10186     easing: 'easeOut',
10187     duration: .5,
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192          * @param {Object} options (optional) Object literal with any of the Fx config options
10193          * @return {Roo.Element} The Element
10194          */
10195     puff : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198
10199         el.queueFx(o, function(){
10200             this.clearOpacity();
10201             this.show();
10202
10203             // restore values after effect
10204             var r = this.getFxRestore();
10205             var st = this.dom.style;
10206
10207             var after = function(){
10208                 if(o.useDisplay){
10209                     el.setDisplayed(false);
10210                 }else{
10211                     el.hide();
10212                 }
10213
10214                 el.clearOpacity();
10215
10216                 el.setPositioning(r.pos);
10217                 st.width = r.width;
10218                 st.height = r.height;
10219                 st.fontSize = '';
10220                 el.afterFx(o);
10221             };
10222
10223             var width = this.getWidth();
10224             var height = this.getHeight();
10225
10226             arguments.callee.anim = this.fxanim({
10227                     width : {to: this.adjustWidth(width * 2)},
10228                     height : {to: this.adjustHeight(height * 2)},
10229                     points : {by: [-(width * .5), -(height * .5)]},
10230                     opacity : {to: 0},
10231                     fontSize: {to:200, unit: "%"}
10232                 },
10233                 o,
10234                 'motion',
10235                 .5,
10236                 "easeOut", after);
10237         });
10238         return this;
10239     },
10240
10241         /**
10242          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10243          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10244          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10245          * Usage:
10246          *<pre><code>
10247 // default
10248 el.switchOff();
10249
10250 // all config options shown with default values
10251 el.switchOff({
10252     easing: 'easeIn',
10253     duration: .3,
10254     remove: false,
10255     useDisplay: false
10256 });
10257 </code></pre>
10258          * @param {Object} options (optional) Object literal with any of the Fx config options
10259          * @return {Roo.Element} The Element
10260          */
10261     switchOff : function(o){
10262         var el = this.getFxEl();
10263         o = o || {};
10264
10265         el.queueFx(o, function(){
10266             this.clearOpacity();
10267             this.clip();
10268
10269             // restore values after effect
10270             var r = this.getFxRestore();
10271             var st = this.dom.style;
10272
10273             var after = function(){
10274                 if(o.useDisplay){
10275                     el.setDisplayed(false);
10276                 }else{
10277                     el.hide();
10278                 }
10279
10280                 el.clearOpacity();
10281                 el.setPositioning(r.pos);
10282                 st.width = r.width;
10283                 st.height = r.height;
10284
10285                 el.afterFx(o);
10286             };
10287
10288             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10289                 this.clearOpacity();
10290                 (function(){
10291                     this.fxanim({
10292                         height:{to:1},
10293                         points:{by:[0, this.getHeight() * .5]}
10294                     }, o, 'motion', 0.3, 'easeIn', after);
10295                 }).defer(100, this);
10296             });
10297         });
10298         return this;
10299     },
10300
10301     /**
10302      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10303      * changed using the "attr" config option) and then fading back to the original color. If no original
10304      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10305      * Usage:
10306 <pre><code>
10307 // default: highlight background to yellow
10308 el.highlight();
10309
10310 // custom: highlight foreground text to blue for 2 seconds
10311 el.highlight("0000ff", { attr: 'color', duration: 2 });
10312
10313 // common config options shown with default values
10314 el.highlight("ffff9c", {
10315     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10316     endColor: (current color) or "ffffff",
10317     easing: 'easeIn',
10318     duration: 1
10319 });
10320 </code></pre>
10321      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10322      * @param {Object} options (optional) Object literal with any of the Fx config options
10323      * @return {Roo.Element} The Element
10324      */ 
10325     highlight : function(color, o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330             color = color || "ffff9c";
10331             attr = o.attr || "backgroundColor";
10332
10333             this.clearOpacity();
10334             this.show();
10335
10336             var origColor = this.getColor(attr);
10337             var restoreColor = this.dom.style[attr];
10338             endColor = (o.endColor || origColor) || "ffffff";
10339
10340             var after = function(){
10341                 el.dom.style[attr] = restoreColor;
10342                 el.afterFx(o);
10343             };
10344
10345             var a = {};
10346             a[attr] = {from: color, to: endColor};
10347             arguments.callee.anim = this.fxanim(a,
10348                 o,
10349                 'color',
10350                 1,
10351                 'easeIn', after);
10352         });
10353         return this;
10354     },
10355
10356    /**
10357     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10358     * Usage:
10359 <pre><code>
10360 // default: a single light blue ripple
10361 el.frame();
10362
10363 // custom: 3 red ripples lasting 3 seconds total
10364 el.frame("ff0000", 3, { duration: 3 });
10365
10366 // common config options shown with default values
10367 el.frame("C3DAF9", 1, {
10368     duration: 1 //duration of entire animation (not each individual ripple)
10369     // Note: Easing is not configurable and will be ignored if included
10370 });
10371 </code></pre>
10372     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10373     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10374     * @param {Object} options (optional) Object literal with any of the Fx config options
10375     * @return {Roo.Element} The Element
10376     */
10377     frame : function(color, count, o){
10378         var el = this.getFxEl();
10379         o = o || {};
10380
10381         el.queueFx(o, function(){
10382             color = color || "#C3DAF9";
10383             if(color.length == 6){
10384                 color = "#" + color;
10385             }
10386             count = count || 1;
10387             duration = o.duration || 1;
10388             this.show();
10389
10390             var b = this.getBox();
10391             var animFn = function(){
10392                 var proxy = this.createProxy({
10393
10394                      style:{
10395                         visbility:"hidden",
10396                         position:"absolute",
10397                         "z-index":"35000", // yee haw
10398                         border:"0px solid " + color
10399                      }
10400                   });
10401                 var scale = Roo.isBorderBox ? 2 : 1;
10402                 proxy.animate({
10403                     top:{from:b.y, to:b.y - 20},
10404                     left:{from:b.x, to:b.x - 20},
10405                     borderWidth:{from:0, to:10},
10406                     opacity:{from:1, to:0},
10407                     height:{from:b.height, to:(b.height + (20*scale))},
10408                     width:{from:b.width, to:(b.width + (20*scale))}
10409                 }, duration, function(){
10410                     proxy.remove();
10411                 });
10412                 if(--count > 0){
10413                      animFn.defer((duration/2)*1000, this);
10414                 }else{
10415                     el.afterFx(o);
10416                 }
10417             };
10418             animFn.call(this);
10419         });
10420         return this;
10421     },
10422
10423    /**
10424     * Creates a pause before any subsequent queued effects begin.  If there are
10425     * no effects queued after the pause it will have no effect.
10426     * Usage:
10427 <pre><code>
10428 el.pause(1);
10429 </code></pre>
10430     * @param {Number} seconds The length of time to pause (in seconds)
10431     * @return {Roo.Element} The Element
10432     */
10433     pause : function(seconds){
10434         var el = this.getFxEl();
10435         var o = {};
10436
10437         el.queueFx(o, function(){
10438             setTimeout(function(){
10439                 el.afterFx(o);
10440             }, seconds * 1000);
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade in from opacity 0 to 100%
10451 el.fadeIn();
10452
10453 // custom: fade in from opacity 0 to 75% over 2 seconds
10454 el.fadeIn({ endOpacity: .75, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeIn({
10458     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461 });
10462 </code></pre>
10463     * @param {Object} options (optional) Object literal with any of the Fx config options
10464     * @return {Roo.Element} The Element
10465     */
10466     fadeIn : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469         el.queueFx(o, function(){
10470             this.setOpacity(0);
10471             this.fixDisplay();
10472             this.dom.style.visibility = 'visible';
10473             var to = o.endOpacity || 1;
10474             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10475                 o, null, .5, "easeOut", function(){
10476                 if(to == 1){
10477                     this.clearOpacity();
10478                 }
10479                 el.afterFx(o);
10480             });
10481         });
10482         return this;
10483     },
10484
10485    /**
10486     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10487     * using the "endOpacity" config option.
10488     * Usage:
10489 <pre><code>
10490 // default: fade out from the element's current opacity to 0
10491 el.fadeOut();
10492
10493 // custom: fade out from the element's current opacity to 25% over 2 seconds
10494 el.fadeOut({ endOpacity: .25, duration: 2});
10495
10496 // common config options shown with default values
10497 el.fadeOut({
10498     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10499     easing: 'easeOut',
10500     duration: .5
10501     remove: false,
10502     useDisplay: false
10503 });
10504 </code></pre>
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     fadeOut : function(o){
10509         var el = this.getFxEl();
10510         o = o || {};
10511         el.queueFx(o, function(){
10512             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10513                 o, null, .5, "easeOut", function(){
10514                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10515                      this.dom.style.display = "none";
10516                 }else{
10517                      this.dom.style.visibility = "hidden";
10518                 }
10519                 this.clearOpacity();
10520                 el.afterFx(o);
10521             });
10522         });
10523         return this;
10524     },
10525
10526    /**
10527     * Animates the transition of an element's dimensions from a starting height/width
10528     * to an ending height/width.
10529     * Usage:
10530 <pre><code>
10531 // change height and width to 100x100 pixels
10532 el.scale(100, 100);
10533
10534 // common config options shown with default values.  The height and width will default to
10535 // the element's existing values if passed as null.
10536 el.scale(
10537     [element's width],
10538     [element's height], {
10539     easing: 'easeOut',
10540     duration: .35
10541 });
10542 </code></pre>
10543     * @param {Number} width  The new width (pass undefined to keep the original width)
10544     * @param {Number} height  The new height (pass undefined to keep the original height)
10545     * @param {Object} options (optional) Object literal with any of the Fx config options
10546     * @return {Roo.Element} The Element
10547     */
10548     scale : function(w, h, o){
10549         this.shift(Roo.apply({}, o, {
10550             width: w,
10551             height: h
10552         }));
10553         return this;
10554     },
10555
10556    /**
10557     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10558     * Any of these properties not specified in the config object will not be changed.  This effect 
10559     * requires that at least one new dimension, position or opacity setting must be passed in on
10560     * the config object in order for the function to have any effect.
10561     * Usage:
10562 <pre><code>
10563 // slide the element horizontally to x position 200 while changing the height and opacity
10564 el.shift({ x: 200, height: 50, opacity: .8 });
10565
10566 // common config options shown with default values.
10567 el.shift({
10568     width: [element's width],
10569     height: [element's height],
10570     x: [element's x position],
10571     y: [element's y position],
10572     opacity: [element's opacity],
10573     easing: 'easeOut',
10574     duration: .35
10575 });
10576 </code></pre>
10577     * @param {Object} options  Object literal with any of the Fx config options
10578     * @return {Roo.Element} The Element
10579     */
10580     shift : function(o){
10581         var el = this.getFxEl();
10582         o = o || {};
10583         el.queueFx(o, function(){
10584             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10585             if(w !== undefined){
10586                 a.width = {to: this.adjustWidth(w)};
10587             }
10588             if(h !== undefined){
10589                 a.height = {to: this.adjustHeight(h)};
10590             }
10591             if(x !== undefined || y !== undefined){
10592                 a.points = {to: [
10593                     x !== undefined ? x : this.getX(),
10594                     y !== undefined ? y : this.getY()
10595                 ]};
10596             }
10597             if(op !== undefined){
10598                 a.opacity = {to: op};
10599             }
10600             if(o.xy !== undefined){
10601                 a.points = {to: o.xy};
10602             }
10603             arguments.callee.anim = this.fxanim(a,
10604                 o, 'motion', .35, "easeOut", function(){
10605                 el.afterFx(o);
10606             });
10607         });
10608         return this;
10609     },
10610
10611         /**
10612          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10613          * ending point of the effect.
10614          * Usage:
10615          *<pre><code>
10616 // default: slide the element downward while fading out
10617 el.ghost();
10618
10619 // custom: slide the element out to the right with a 2-second duration
10620 el.ghost('r', { duration: 2 });
10621
10622 // common config options shown with default values
10623 el.ghost('b', {
10624     easing: 'easeOut',
10625     duration: .5
10626     remove: false,
10627     useDisplay: false
10628 });
10629 </code></pre>
10630          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10631          * @param {Object} options (optional) Object literal with any of the Fx config options
10632          * @return {Roo.Element} The Element
10633          */
10634     ghost : function(anchor, o){
10635         var el = this.getFxEl();
10636         o = o || {};
10637
10638         el.queueFx(o, function(){
10639             anchor = anchor || "b";
10640
10641             // restore values after effect
10642             var r = this.getFxRestore();
10643             var w = this.getWidth(),
10644                 h = this.getHeight();
10645
10646             var st = this.dom.style;
10647
10648             var after = function(){
10649                 if(o.useDisplay){
10650                     el.setDisplayed(false);
10651                 }else{
10652                     el.hide();
10653                 }
10654
10655                 el.clearOpacity();
10656                 el.setPositioning(r.pos);
10657                 st.width = r.width;
10658                 st.height = r.height;
10659
10660                 el.afterFx(o);
10661             };
10662
10663             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10664             switch(anchor.toLowerCase()){
10665                 case "t":
10666                     pt.by = [0, -h];
10667                 break;
10668                 case "l":
10669                     pt.by = [-w, 0];
10670                 break;
10671                 case "r":
10672                     pt.by = [w, 0];
10673                 break;
10674                 case "b":
10675                     pt.by = [0, h];
10676                 break;
10677                 case "tl":
10678                     pt.by = [-w, -h];
10679                 break;
10680                 case "bl":
10681                     pt.by = [-w, h];
10682                 break;
10683                 case "br":
10684                     pt.by = [w, h];
10685                 break;
10686                 case "tr":
10687                     pt.by = [w, -h];
10688                 break;
10689             }
10690
10691             arguments.callee.anim = this.fxanim(a,
10692                 o,
10693                 'motion',
10694                 .5,
10695                 "easeOut", after);
10696         });
10697         return this;
10698     },
10699
10700         /**
10701          * Ensures that all effects queued after syncFx is called on the element are
10702          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10703          * @return {Roo.Element} The Element
10704          */
10705     syncFx : function(){
10706         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10707             block : false,
10708             concurrent : true,
10709             stopFx : false
10710         });
10711         return this;
10712     },
10713
10714         /**
10715          * Ensures that all effects queued after sequenceFx is called on the element are
10716          * run in sequence.  This is the opposite of {@link #syncFx}.
10717          * @return {Roo.Element} The Element
10718          */
10719     sequenceFx : function(){
10720         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10721             block : false,
10722             concurrent : false,
10723             stopFx : false
10724         });
10725         return this;
10726     },
10727
10728         /* @private */
10729     nextFx : function(){
10730         var ef = this.fxQueue[0];
10731         if(ef){
10732             ef.call(this);
10733         }
10734     },
10735
10736         /**
10737          * Returns true if the element has any effects actively running or queued, else returns false.
10738          * @return {Boolean} True if element has active effects, else false
10739          */
10740     hasActiveFx : function(){
10741         return this.fxQueue && this.fxQueue[0];
10742     },
10743
10744         /**
10745          * Stops any running effects and clears the element's internal effects queue if it contains
10746          * any additional effects that haven't started yet.
10747          * @return {Roo.Element} The Element
10748          */
10749     stopFx : function(){
10750         if(this.hasActiveFx()){
10751             var cur = this.fxQueue[0];
10752             if(cur && cur.anim && cur.anim.isAnimated()){
10753                 this.fxQueue = [cur]; // clear out others
10754                 cur.anim.stop(true);
10755             }
10756         }
10757         return this;
10758     },
10759
10760         /* @private */
10761     beforeFx : function(o){
10762         if(this.hasActiveFx() && !o.concurrent){
10763            if(o.stopFx){
10764                this.stopFx();
10765                return true;
10766            }
10767            return false;
10768         }
10769         return true;
10770     },
10771
10772         /**
10773          * Returns true if the element is currently blocking so that no other effect can be queued
10774          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10775          * used to ensure that an effect initiated by a user action runs to completion prior to the
10776          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10777          * @return {Boolean} True if blocking, else false
10778          */
10779     hasFxBlock : function(){
10780         var q = this.fxQueue;
10781         return q && q[0] && q[0].block;
10782     },
10783
10784         /* @private */
10785     queueFx : function(o, fn){
10786         if(!this.fxQueue){
10787             this.fxQueue = [];
10788         }
10789         if(!this.hasFxBlock()){
10790             Roo.applyIf(o, this.fxDefaults);
10791             if(!o.concurrent){
10792                 var run = this.beforeFx(o);
10793                 fn.block = o.block;
10794                 this.fxQueue.push(fn);
10795                 if(run){
10796                     this.nextFx();
10797                 }
10798             }else{
10799                 fn.call(this);
10800             }
10801         }
10802         return this;
10803     },
10804
10805         /* @private */
10806     fxWrap : function(pos, o, vis){
10807         var wrap;
10808         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10809             var wrapXY;
10810             if(o.fixPosition){
10811                 wrapXY = this.getXY();
10812             }
10813             var div = document.createElement("div");
10814             div.style.visibility = vis;
10815             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10816             wrap.setPositioning(pos);
10817             if(wrap.getStyle("position") == "static"){
10818                 wrap.position("relative");
10819             }
10820             this.clearPositioning('auto');
10821             wrap.clip();
10822             wrap.dom.appendChild(this.dom);
10823             if(wrapXY){
10824                 wrap.setXY(wrapXY);
10825             }
10826         }
10827         return wrap;
10828     },
10829
10830         /* @private */
10831     fxUnwrap : function(wrap, pos, o){
10832         this.clearPositioning();
10833         this.setPositioning(pos);
10834         if(!o.wrap){
10835             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10836             wrap.remove();
10837         }
10838     },
10839
10840         /* @private */
10841     getFxRestore : function(){
10842         var st = this.dom.style;
10843         return {pos: this.getPositioning(), width: st.width, height : st.height};
10844     },
10845
10846         /* @private */
10847     afterFx : function(o){
10848         if(o.afterStyle){
10849             this.applyStyles(o.afterStyle);
10850         }
10851         if(o.afterCls){
10852             this.addClass(o.afterCls);
10853         }
10854         if(o.remove === true){
10855             this.remove();
10856         }
10857         Roo.callback(o.callback, o.scope, [this]);
10858         if(!o.concurrent){
10859             this.fxQueue.shift();
10860             this.nextFx();
10861         }
10862     },
10863
10864         /* @private */
10865     getFxEl : function(){ // support for composite element fx
10866         return Roo.get(this.dom);
10867     },
10868
10869         /* @private */
10870     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10871         animType = animType || 'run';
10872         opt = opt || {};
10873         var anim = Roo.lib.Anim[animType](
10874             this.dom, args,
10875             (opt.duration || defaultDur) || .35,
10876             (opt.easing || defaultEase) || 'easeOut',
10877             function(){
10878                 Roo.callback(cb, this);
10879             },
10880             this
10881         );
10882         opt.anim = anim;
10883         return anim;
10884     }
10885 };
10886
10887 // backwords compat
10888 Roo.Fx.resize = Roo.Fx.scale;
10889
10890 //When included, Roo.Fx is automatically applied to Element so that all basic
10891 //effects are available directly via the Element API
10892 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10893  * Based on:
10894  * Ext JS Library 1.1.1
10895  * Copyright(c) 2006-2007, Ext JS, LLC.
10896  *
10897  * Originally Released Under LGPL - original licence link has changed is not relivant.
10898  *
10899  * Fork - LGPL
10900  * <script type="text/javascript">
10901  */
10902
10903
10904 /**
10905  * @class Roo.CompositeElement
10906  * Standard composite class. Creates a Roo.Element for every element in the collection.
10907  * <br><br>
10908  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10909  * actions will be performed on all the elements in this collection.</b>
10910  * <br><br>
10911  * All methods return <i>this</i> and can be chained.
10912  <pre><code>
10913  var els = Roo.select("#some-el div.some-class", true);
10914  // or select directly from an existing element
10915  var el = Roo.get('some-el');
10916  el.select('div.some-class', true);
10917
10918  els.setWidth(100); // all elements become 100 width
10919  els.hide(true); // all elements fade out and hide
10920  // or
10921  els.setWidth(100).hide(true);
10922  </code></pre>
10923  */
10924 Roo.CompositeElement = function(els){
10925     this.elements = [];
10926     this.addElements(els);
10927 };
10928 Roo.CompositeElement.prototype = {
10929     isComposite: true,
10930     addElements : function(els){
10931         if(!els) return this;
10932         if(typeof els == "string"){
10933             els = Roo.Element.selectorFunction(els);
10934         }
10935         var yels = this.elements;
10936         var index = yels.length-1;
10937         for(var i = 0, len = els.length; i < len; i++) {
10938                 yels[++index] = Roo.get(els[i]);
10939         }
10940         return this;
10941     },
10942
10943     /**
10944     * Clears this composite and adds the elements returned by the passed selector.
10945     * @param {String/Array} els A string CSS selector, an array of elements or an element
10946     * @return {CompositeElement} this
10947     */
10948     fill : function(els){
10949         this.elements = [];
10950         this.add(els);
10951         return this;
10952     },
10953
10954     /**
10955     * Filters this composite to only elements that match the passed selector.
10956     * @param {String} selector A string CSS selector
10957     * @param {Boolean} inverse return inverse filter (not matches)
10958     * @return {CompositeElement} this
10959     */
10960     filter : function(selector, inverse){
10961         var els = [];
10962         inverse = inverse || false;
10963         this.each(function(el){
10964             var match = inverse ? !el.is(selector) : el.is(selector);
10965             if(match){
10966                 els[els.length] = el.dom;
10967             }
10968         });
10969         this.fill(els);
10970         return this;
10971     },
10972
10973     invoke : function(fn, args){
10974         var els = this.elements;
10975         for(var i = 0, len = els.length; i < len; i++) {
10976                 Roo.Element.prototype[fn].apply(els[i], args);
10977         }
10978         return this;
10979     },
10980     /**
10981     * Adds elements to this composite.
10982     * @param {String/Array} els A string CSS selector, an array of elements or an element
10983     * @return {CompositeElement} this
10984     */
10985     add : function(els){
10986         if(typeof els == "string"){
10987             this.addElements(Roo.Element.selectorFunction(els));
10988         }else if(els.length !== undefined){
10989             this.addElements(els);
10990         }else{
10991             this.addElements([els]);
10992         }
10993         return this;
10994     },
10995     /**
10996     * Calls the passed function passing (el, this, index) for each element in this composite.
10997     * @param {Function} fn The function to call
10998     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10999     * @return {CompositeElement} this
11000     */
11001     each : function(fn, scope){
11002         var els = this.elements;
11003         for(var i = 0, len = els.length; i < len; i++){
11004             if(fn.call(scope || els[i], els[i], this, i) === false) {
11005                 break;
11006             }
11007         }
11008         return this;
11009     },
11010
11011     /**
11012      * Returns the Element object at the specified index
11013      * @param {Number} index
11014      * @return {Roo.Element}
11015      */
11016     item : function(index){
11017         return this.elements[index] || null;
11018     },
11019
11020     /**
11021      * Returns the first Element
11022      * @return {Roo.Element}
11023      */
11024     first : function(){
11025         return this.item(0);
11026     },
11027
11028     /**
11029      * Returns the last Element
11030      * @return {Roo.Element}
11031      */
11032     last : function(){
11033         return this.item(this.elements.length-1);
11034     },
11035
11036     /**
11037      * Returns the number of elements in this composite
11038      * @return Number
11039      */
11040     getCount : function(){
11041         return this.elements.length;
11042     },
11043
11044     /**
11045      * Returns true if this composite contains the passed element
11046      * @return Boolean
11047      */
11048     contains : function(el){
11049         return this.indexOf(el) !== -1;
11050     },
11051
11052     /**
11053      * Returns true if this composite contains the passed element
11054      * @return Boolean
11055      */
11056     indexOf : function(el){
11057         return this.elements.indexOf(Roo.get(el));
11058     },
11059
11060
11061     /**
11062     * Removes the specified element(s).
11063     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11064     * or an array of any of those.
11065     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11066     * @return {CompositeElement} this
11067     */
11068     removeElement : function(el, removeDom){
11069         if(el instanceof Array){
11070             for(var i = 0, len = el.length; i < len; i++){
11071                 this.removeElement(el[i]);
11072             }
11073             return this;
11074         }
11075         var index = typeof el == 'number' ? el : this.indexOf(el);
11076         if(index !== -1){
11077             if(removeDom){
11078                 var d = this.elements[index];
11079                 if(d.dom){
11080                     d.remove();
11081                 }else{
11082                     d.parentNode.removeChild(d);
11083                 }
11084             }
11085             this.elements.splice(index, 1);
11086         }
11087         return this;
11088     },
11089
11090     /**
11091     * Replaces the specified element with the passed element.
11092     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11093     * to replace.
11094     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11095     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11096     * @return {CompositeElement} this
11097     */
11098     replaceElement : function(el, replacement, domReplace){
11099         var index = typeof el == 'number' ? el : this.indexOf(el);
11100         if(index !== -1){
11101             if(domReplace){
11102                 this.elements[index].replaceWith(replacement);
11103             }else{
11104                 this.elements.splice(index, 1, Roo.get(replacement))
11105             }
11106         }
11107         return this;
11108     },
11109
11110     /**
11111      * Removes all elements.
11112      */
11113     clear : function(){
11114         this.elements = [];
11115     }
11116 };
11117 (function(){
11118     Roo.CompositeElement.createCall = function(proto, fnName){
11119         if(!proto[fnName]){
11120             proto[fnName] = function(){
11121                 return this.invoke(fnName, arguments);
11122             };
11123         }
11124     };
11125     for(var fnName in Roo.Element.prototype){
11126         if(typeof Roo.Element.prototype[fnName] == "function"){
11127             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11128         }
11129     };
11130 })();
11131 /*
11132  * Based on:
11133  * Ext JS Library 1.1.1
11134  * Copyright(c) 2006-2007, Ext JS, LLC.
11135  *
11136  * Originally Released Under LGPL - original licence link has changed is not relivant.
11137  *
11138  * Fork - LGPL
11139  * <script type="text/javascript">
11140  */
11141
11142 /**
11143  * @class Roo.CompositeElementLite
11144  * @extends Roo.CompositeElement
11145  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11146  <pre><code>
11147  var els = Roo.select("#some-el div.some-class");
11148  // or select directly from an existing element
11149  var el = Roo.get('some-el');
11150  el.select('div.some-class');
11151
11152  els.setWidth(100); // all elements become 100 width
11153  els.hide(true); // all elements fade out and hide
11154  // or
11155  els.setWidth(100).hide(true);
11156  </code></pre><br><br>
11157  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11158  * actions will be performed on all the elements in this collection.</b>
11159  */
11160 Roo.CompositeElementLite = function(els){
11161     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11162     this.el = new Roo.Element.Flyweight();
11163 };
11164 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11165     addElements : function(els){
11166         if(els){
11167             if(els instanceof Array){
11168                 this.elements = this.elements.concat(els);
11169             }else{
11170                 var yels = this.elements;
11171                 var index = yels.length-1;
11172                 for(var i = 0, len = els.length; i < len; i++) {
11173                     yels[++index] = els[i];
11174                 }
11175             }
11176         }
11177         return this;
11178     },
11179     invoke : function(fn, args){
11180         var els = this.elements;
11181         var el = this.el;
11182         for(var i = 0, len = els.length; i < len; i++) {
11183             el.dom = els[i];
11184                 Roo.Element.prototype[fn].apply(el, args);
11185         }
11186         return this;
11187     },
11188     /**
11189      * Returns a flyweight Element of the dom element object at the specified index
11190      * @param {Number} index
11191      * @return {Roo.Element}
11192      */
11193     item : function(index){
11194         if(!this.elements[index]){
11195             return null;
11196         }
11197         this.el.dom = this.elements[index];
11198         return this.el;
11199     },
11200
11201     // fixes scope with flyweight
11202     addListener : function(eventName, handler, scope, opt){
11203         var els = this.elements;
11204         for(var i = 0, len = els.length; i < len; i++) {
11205             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11206         }
11207         return this;
11208     },
11209
11210     /**
11211     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11212     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11213     * a reference to the dom node, use el.dom.</b>
11214     * @param {Function} fn The function to call
11215     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11216     * @return {CompositeElement} this
11217     */
11218     each : function(fn, scope){
11219         var els = this.elements;
11220         var el = this.el;
11221         for(var i = 0, len = els.length; i < len; i++){
11222             el.dom = els[i];
11223                 if(fn.call(scope || el, el, this, i) === false){
11224                 break;
11225             }
11226         }
11227         return this;
11228     },
11229
11230     indexOf : function(el){
11231         return this.elements.indexOf(Roo.getDom(el));
11232     },
11233
11234     replaceElement : function(el, replacement, domReplace){
11235         var index = typeof el == 'number' ? el : this.indexOf(el);
11236         if(index !== -1){
11237             replacement = Roo.getDom(replacement);
11238             if(domReplace){
11239                 var d = this.elements[index];
11240                 d.parentNode.insertBefore(replacement, d);
11241                 d.parentNode.removeChild(d);
11242             }
11243             this.elements.splice(index, 1, replacement);
11244         }
11245         return this;
11246     }
11247 });
11248 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11249
11250 /*
11251  * Based on:
11252  * Ext JS Library 1.1.1
11253  * Copyright(c) 2006-2007, Ext JS, LLC.
11254  *
11255  * Originally Released Under LGPL - original licence link has changed is not relivant.
11256  *
11257  * Fork - LGPL
11258  * <script type="text/javascript">
11259  */
11260
11261  
11262
11263 /**
11264  * @class Roo.data.Connection
11265  * @extends Roo.util.Observable
11266  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11267  * either to a configured URL, or to a URL specified at request time.<br><br>
11268  * <p>
11269  * Requests made by this class are asynchronous, and will return immediately. No data from
11270  * the server will be available to the statement immediately following the {@link #request} call.
11271  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11272  * <p>
11273  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11274  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11275  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11276  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11277  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11278  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11279  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11280  * standard DOM methods.
11281  * @constructor
11282  * @param {Object} config a configuration object.
11283  */
11284 Roo.data.Connection = function(config){
11285     Roo.apply(this, config);
11286     this.addEvents({
11287         /**
11288          * @event beforerequest
11289          * Fires before a network request is made to retrieve a data object.
11290          * @param {Connection} conn This Connection object.
11291          * @param {Object} options The options config object passed to the {@link #request} method.
11292          */
11293         "beforerequest" : true,
11294         /**
11295          * @event requestcomplete
11296          * Fires if the request was successfully completed.
11297          * @param {Connection} conn This Connection object.
11298          * @param {Object} response The XHR object containing the response data.
11299          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11300          * @param {Object} options The options config object passed to the {@link #request} method.
11301          */
11302         "requestcomplete" : true,
11303         /**
11304          * @event requestexception
11305          * Fires if an error HTTP status was returned from the server.
11306          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11307          * @param {Connection} conn This Connection object.
11308          * @param {Object} response The XHR object containing the response data.
11309          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11310          * @param {Object} options The options config object passed to the {@link #request} method.
11311          */
11312         "requestexception" : true
11313     });
11314     Roo.data.Connection.superclass.constructor.call(this);
11315 };
11316
11317 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11318     /**
11319      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11320      */
11321     /**
11322      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11323      * extra parameters to each request made by this object. (defaults to undefined)
11324      */
11325     /**
11326      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11327      *  to each request made by this object. (defaults to undefined)
11328      */
11329     /**
11330      * @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)
11331      */
11332     /**
11333      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11334      */
11335     timeout : 30000,
11336     /**
11337      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11338      * @type Boolean
11339      */
11340     autoAbort:false,
11341
11342     /**
11343      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11344      * @type Boolean
11345      */
11346     disableCaching: true,
11347
11348     /**
11349      * Sends an HTTP request to a remote server.
11350      * @param {Object} options An object which may contain the following properties:<ul>
11351      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11352      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11353      * request, a url encoded string or a function to call to get either.</li>
11354      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11355      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11356      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11357      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11358      * <li>options {Object} The parameter to the request call.</li>
11359      * <li>success {Boolean} True if the request succeeded.</li>
11360      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11361      * </ul></li>
11362      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11363      * The callback is passed the following parameters:<ul>
11364      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11365      * <li>options {Object} The parameter to the request call.</li>
11366      * </ul></li>
11367      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11368      * The callback is passed the following parameters:<ul>
11369      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11370      * <li>options {Object} The parameter to the request call.</li>
11371      * </ul></li>
11372      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11373      * for the callback function. Defaults to the browser window.</li>
11374      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11375      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11376      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11377      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11378      * params for the post data. Any params will be appended to the URL.</li>
11379      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11380      * </ul>
11381      * @return {Number} transactionId
11382      */
11383     request : function(o){
11384         if(this.fireEvent("beforerequest", this, o) !== false){
11385             var p = o.params;
11386
11387             if(typeof p == "function"){
11388                 p = p.call(o.scope||window, o);
11389             }
11390             if(typeof p == "object"){
11391                 p = Roo.urlEncode(o.params);
11392             }
11393             if(this.extraParams){
11394                 var extras = Roo.urlEncode(this.extraParams);
11395                 p = p ? (p + '&' + extras) : extras;
11396             }
11397
11398             var url = o.url || this.url;
11399             if(typeof url == 'function'){
11400                 url = url.call(o.scope||window, o);
11401             }
11402
11403             if(o.form){
11404                 var form = Roo.getDom(o.form);
11405                 url = url || form.action;
11406
11407                 var enctype = form.getAttribute("enctype");
11408                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11409                     return this.doFormUpload(o, p, url);
11410                 }
11411                 var f = Roo.lib.Ajax.serializeForm(form);
11412                 p = p ? (p + '&' + f) : f;
11413             }
11414
11415             var hs = o.headers;
11416             if(this.defaultHeaders){
11417                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11418                 if(!o.headers){
11419                     o.headers = hs;
11420                 }
11421             }
11422
11423             var cb = {
11424                 success: this.handleResponse,
11425                 failure: this.handleFailure,
11426                 scope: this,
11427                 argument: {options: o},
11428                 timeout : o.timeout || this.timeout
11429             };
11430
11431             var method = o.method||this.method||(p ? "POST" : "GET");
11432
11433             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11434                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11435             }
11436
11437             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11438                 if(o.autoAbort){
11439                     this.abort();
11440                 }
11441             }else if(this.autoAbort !== false){
11442                 this.abort();
11443             }
11444
11445             if((method == 'GET' && p) || o.xmlData){
11446                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11447                 p = '';
11448             }
11449             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11450             return this.transId;
11451         }else{
11452             Roo.callback(o.callback, o.scope, [o, null, null]);
11453             return null;
11454         }
11455     },
11456
11457     /**
11458      * Determine whether this object has a request outstanding.
11459      * @param {Number} transactionId (Optional) defaults to the last transaction
11460      * @return {Boolean} True if there is an outstanding request.
11461      */
11462     isLoading : function(transId){
11463         if(transId){
11464             return Roo.lib.Ajax.isCallInProgress(transId);
11465         }else{
11466             return this.transId ? true : false;
11467         }
11468     },
11469
11470     /**
11471      * Aborts any outstanding request.
11472      * @param {Number} transactionId (Optional) defaults to the last transaction
11473      */
11474     abort : function(transId){
11475         if(transId || this.isLoading()){
11476             Roo.lib.Ajax.abort(transId || this.transId);
11477         }
11478     },
11479
11480     // private
11481     handleResponse : function(response){
11482         this.transId = false;
11483         var options = response.argument.options;
11484         response.argument = options ? options.argument : null;
11485         this.fireEvent("requestcomplete", this, response, options);
11486         Roo.callback(options.success, options.scope, [response, options]);
11487         Roo.callback(options.callback, options.scope, [options, true, response]);
11488     },
11489
11490     // private
11491     handleFailure : function(response, e){
11492         this.transId = false;
11493         var options = response.argument.options;
11494         response.argument = options ? options.argument : null;
11495         this.fireEvent("requestexception", this, response, options, e);
11496         Roo.callback(options.failure, options.scope, [response, options]);
11497         Roo.callback(options.callback, options.scope, [options, false, response]);
11498     },
11499
11500     // private
11501     doFormUpload : function(o, ps, url){
11502         var id = Roo.id();
11503         var frame = document.createElement('iframe');
11504         frame.id = id;
11505         frame.name = id;
11506         frame.className = 'x-hidden';
11507         if(Roo.isIE){
11508             frame.src = Roo.SSL_SECURE_URL;
11509         }
11510         document.body.appendChild(frame);
11511
11512         if(Roo.isIE){
11513            document.frames[id].name = id;
11514         }
11515
11516         var form = Roo.getDom(o.form);
11517         form.target = id;
11518         form.method = 'POST';
11519         form.enctype = form.encoding = 'multipart/form-data';
11520         if(url){
11521             form.action = url;
11522         }
11523
11524         var hiddens, hd;
11525         if(ps){ // add dynamic params
11526             hiddens = [];
11527             ps = Roo.urlDecode(ps, false);
11528             for(var k in ps){
11529                 if(ps.hasOwnProperty(k)){
11530                     hd = document.createElement('input');
11531                     hd.type = 'hidden';
11532                     hd.name = k;
11533                     hd.value = ps[k];
11534                     form.appendChild(hd);
11535                     hiddens.push(hd);
11536                 }
11537             }
11538         }
11539
11540         function cb(){
11541             var r = {  // bogus response object
11542                 responseText : '',
11543                 responseXML : null
11544             };
11545
11546             r.argument = o ? o.argument : null;
11547
11548             try { //
11549                 var doc;
11550                 if(Roo.isIE){
11551                     doc = frame.contentWindow.document;
11552                 }else {
11553                     doc = (frame.contentDocument || window.frames[id].document);
11554                 }
11555                 if(doc && doc.body){
11556                     r.responseText = doc.body.innerHTML;
11557                 }
11558                 if(doc && doc.XMLDocument){
11559                     r.responseXML = doc.XMLDocument;
11560                 }else {
11561                     r.responseXML = doc;
11562                 }
11563             }
11564             catch(e) {
11565                 // ignore
11566             }
11567
11568             Roo.EventManager.removeListener(frame, 'load', cb, this);
11569
11570             this.fireEvent("requestcomplete", this, r, o);
11571             Roo.callback(o.success, o.scope, [r, o]);
11572             Roo.callback(o.callback, o.scope, [o, true, r]);
11573
11574             setTimeout(function(){document.body.removeChild(frame);}, 100);
11575         }
11576
11577         Roo.EventManager.on(frame, 'load', cb, this);
11578         form.submit();
11579
11580         if(hiddens){ // remove dynamic params
11581             for(var i = 0, len = hiddens.length; i < len; i++){
11582                 form.removeChild(hiddens[i]);
11583             }
11584         }
11585     }
11586 });
11587 /*
11588  * Based on:
11589  * Ext JS Library 1.1.1
11590  * Copyright(c) 2006-2007, Ext JS, LLC.
11591  *
11592  * Originally Released Under LGPL - original licence link has changed is not relivant.
11593  *
11594  * Fork - LGPL
11595  * <script type="text/javascript">
11596  */
11597  
11598 /**
11599  * Global Ajax request class.
11600  * 
11601  * @class Roo.Ajax
11602  * @extends Roo.data.Connection
11603  * @static
11604  * 
11605  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11606  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11607  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11608  * @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)
11609  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11610  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11611  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11612  */
11613 Roo.Ajax = new Roo.data.Connection({
11614     // fix up the docs
11615     /**
11616      * @scope Roo.Ajax
11617      * @type {Boolear} 
11618      */
11619     autoAbort : false,
11620
11621     /**
11622      * Serialize the passed form into a url encoded string
11623      * @scope Roo.Ajax
11624      * @param {String/HTMLElement} form
11625      * @return {String}
11626      */
11627     serializeForm : function(form){
11628         return Roo.lib.Ajax.serializeForm(form);
11629     }
11630 });/*
11631  * Based on:
11632  * Ext JS Library 1.1.1
11633  * Copyright(c) 2006-2007, Ext JS, LLC.
11634  *
11635  * Originally Released Under LGPL - original licence link has changed is not relivant.
11636  *
11637  * Fork - LGPL
11638  * <script type="text/javascript">
11639  */
11640
11641  
11642 /**
11643  * @class Roo.UpdateManager
11644  * @extends Roo.util.Observable
11645  * Provides AJAX-style update for Element object.<br><br>
11646  * Usage:<br>
11647  * <pre><code>
11648  * // Get it from a Roo.Element object
11649  * var el = Roo.get("foo");
11650  * var mgr = el.getUpdateManager();
11651  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11652  * ...
11653  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11654  * <br>
11655  * // or directly (returns the same UpdateManager instance)
11656  * var mgr = new Roo.UpdateManager("myElementId");
11657  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11658  * mgr.on("update", myFcnNeedsToKnow);
11659  * <br>
11660    // short handed call directly from the element object
11661    Roo.get("foo").load({
11662         url: "bar.php",
11663         scripts:true,
11664         params: "for=bar",
11665         text: "Loading Foo..."
11666    });
11667  * </code></pre>
11668  * @constructor
11669  * Create new UpdateManager directly.
11670  * @param {String/HTMLElement/Roo.Element} el The element to update
11671  * @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).
11672  */
11673 Roo.UpdateManager = function(el, forceNew){
11674     el = Roo.get(el);
11675     if(!forceNew && el.updateManager){
11676         return el.updateManager;
11677     }
11678     /**
11679      * The Element object
11680      * @type Roo.Element
11681      */
11682     this.el = el;
11683     /**
11684      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11685      * @type String
11686      */
11687     this.defaultUrl = null;
11688
11689     this.addEvents({
11690         /**
11691          * @event beforeupdate
11692          * Fired before an update is made, return false from your handler and the update is cancelled.
11693          * @param {Roo.Element} el
11694          * @param {String/Object/Function} url
11695          * @param {String/Object} params
11696          */
11697         "beforeupdate": true,
11698         /**
11699          * @event update
11700          * Fired after successful update is made.
11701          * @param {Roo.Element} el
11702          * @param {Object} oResponseObject The response Object
11703          */
11704         "update": true,
11705         /**
11706          * @event failure
11707          * Fired on update failure.
11708          * @param {Roo.Element} el
11709          * @param {Object} oResponseObject The response Object
11710          */
11711         "failure": true
11712     });
11713     var d = Roo.UpdateManager.defaults;
11714     /**
11715      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11716      * @type String
11717      */
11718     this.sslBlankUrl = d.sslBlankUrl;
11719     /**
11720      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11721      * @type Boolean
11722      */
11723     this.disableCaching = d.disableCaching;
11724     /**
11725      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11726      * @type String
11727      */
11728     this.indicatorText = d.indicatorText;
11729     /**
11730      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11731      * @type String
11732      */
11733     this.showLoadIndicator = d.showLoadIndicator;
11734     /**
11735      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11736      * @type Number
11737      */
11738     this.timeout = d.timeout;
11739
11740     /**
11741      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11742      * @type Boolean
11743      */
11744     this.loadScripts = d.loadScripts;
11745
11746     /**
11747      * Transaction object of current executing transaction
11748      */
11749     this.transaction = null;
11750
11751     /**
11752      * @private
11753      */
11754     this.autoRefreshProcId = null;
11755     /**
11756      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11757      * @type Function
11758      */
11759     this.refreshDelegate = this.refresh.createDelegate(this);
11760     /**
11761      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11762      * @type Function
11763      */
11764     this.updateDelegate = this.update.createDelegate(this);
11765     /**
11766      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11767      * @type Function
11768      */
11769     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11770     /**
11771      * @private
11772      */
11773     this.successDelegate = this.processSuccess.createDelegate(this);
11774     /**
11775      * @private
11776      */
11777     this.failureDelegate = this.processFailure.createDelegate(this);
11778
11779     if(!this.renderer){
11780      /**
11781       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11782       */
11783     this.renderer = new Roo.UpdateManager.BasicRenderer();
11784     }
11785     
11786     Roo.UpdateManager.superclass.constructor.call(this);
11787 };
11788
11789 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11790     /**
11791      * Get the Element this UpdateManager is bound to
11792      * @return {Roo.Element} The element
11793      */
11794     getEl : function(){
11795         return this.el;
11796     },
11797     /**
11798      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11799      * @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:
11800 <pre><code>
11801 um.update({<br/>
11802     url: "your-url.php",<br/>
11803     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11804     callback: yourFunction,<br/>
11805     scope: yourObject, //(optional scope)  <br/>
11806     discardUrl: false, <br/>
11807     nocache: false,<br/>
11808     text: "Loading...",<br/>
11809     timeout: 30,<br/>
11810     scripts: false<br/>
11811 });
11812 </code></pre>
11813      * The only required property is url. The optional properties nocache, text and scripts
11814      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11815      * @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}
11816      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11817      * @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.
11818      */
11819     update : function(url, params, callback, discardUrl){
11820         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11821             var method = this.method,
11822                 cfg;
11823             if(typeof url == "object"){ // must be config object
11824                 cfg = url;
11825                 url = cfg.url;
11826                 params = params || cfg.params;
11827                 callback = callback || cfg.callback;
11828                 discardUrl = discardUrl || cfg.discardUrl;
11829                 if(callback && cfg.scope){
11830                     callback = callback.createDelegate(cfg.scope);
11831                 }
11832                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11833                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11834                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11835                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11836                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11837             }
11838             this.showLoading();
11839             if(!discardUrl){
11840                 this.defaultUrl = url;
11841             }
11842             if(typeof url == "function"){
11843                 url = url.call(this);
11844             }
11845
11846             method = method || (params ? "POST" : "GET");
11847             if(method == "GET"){
11848                 url = this.prepareUrl(url);
11849             }
11850
11851             var o = Roo.apply(cfg ||{}, {
11852                 url : url,
11853                 params: params,
11854                 success: this.successDelegate,
11855                 failure: this.failureDelegate,
11856                 callback: undefined,
11857                 timeout: (this.timeout*1000),
11858                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11859             });
11860             Roo.log("updated manager called with timeout of " + o.timeout);
11861             this.transaction = Roo.Ajax.request(o);
11862         }
11863     },
11864
11865     /**
11866      * 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.
11867      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11868      * @param {String/HTMLElement} form The form Id or form element
11869      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11870      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11871      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11872      */
11873     formUpdate : function(form, url, reset, callback){
11874         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11875             if(typeof url == "function"){
11876                 url = url.call(this);
11877             }
11878             form = Roo.getDom(form);
11879             this.transaction = Roo.Ajax.request({
11880                 form: form,
11881                 url:url,
11882                 success: this.successDelegate,
11883                 failure: this.failureDelegate,
11884                 timeout: (this.timeout*1000),
11885                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11886             });
11887             this.showLoading.defer(1, this);
11888         }
11889     },
11890
11891     /**
11892      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11893      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11894      */
11895     refresh : function(callback){
11896         if(this.defaultUrl == null){
11897             return;
11898         }
11899         this.update(this.defaultUrl, null, callback, true);
11900     },
11901
11902     /**
11903      * Set this element to auto refresh.
11904      * @param {Number} interval How often to update (in seconds).
11905      * @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)
11906      * @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}
11907      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11908      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11909      */
11910     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11911         if(refreshNow){
11912             this.update(url || this.defaultUrl, params, callback, true);
11913         }
11914         if(this.autoRefreshProcId){
11915             clearInterval(this.autoRefreshProcId);
11916         }
11917         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11918     },
11919
11920     /**
11921      * Stop auto refresh on this element.
11922      */
11923      stopAutoRefresh : function(){
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926             delete this.autoRefreshProcId;
11927         }
11928     },
11929
11930     isAutoRefreshing : function(){
11931        return this.autoRefreshProcId ? true : false;
11932     },
11933     /**
11934      * Called to update the element to "Loading" state. Override to perform custom action.
11935      */
11936     showLoading : function(){
11937         if(this.showLoadIndicator){
11938             this.el.update(this.indicatorText);
11939         }
11940     },
11941
11942     /**
11943      * Adds unique parameter to query string if disableCaching = true
11944      * @private
11945      */
11946     prepareUrl : function(url){
11947         if(this.disableCaching){
11948             var append = "_dc=" + (new Date().getTime());
11949             if(url.indexOf("?") !== -1){
11950                 url += "&" + append;
11951             }else{
11952                 url += "?" + append;
11953             }
11954         }
11955         return url;
11956     },
11957
11958     /**
11959      * @private
11960      */
11961     processSuccess : function(response){
11962         this.transaction = null;
11963         if(response.argument.form && response.argument.reset){
11964             try{ // put in try/catch since some older FF releases had problems with this
11965                 response.argument.form.reset();
11966             }catch(e){}
11967         }
11968         if(this.loadScripts){
11969             this.renderer.render(this.el, response, this,
11970                 this.updateComplete.createDelegate(this, [response]));
11971         }else{
11972             this.renderer.render(this.el, response, this);
11973             this.updateComplete(response);
11974         }
11975     },
11976
11977     updateComplete : function(response){
11978         this.fireEvent("update", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, true, response);
11981         }
11982     },
11983
11984     /**
11985      * @private
11986      */
11987     processFailure : function(response){
11988         this.transaction = null;
11989         this.fireEvent("failure", this.el, response);
11990         if(typeof response.argument.callback == "function"){
11991             response.argument.callback(this.el, false, response);
11992         }
11993     },
11994
11995     /**
11996      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11997      * @param {Object} renderer The object implementing the render() method
11998      */
11999     setRenderer : function(renderer){
12000         this.renderer = renderer;
12001     },
12002
12003     getRenderer : function(){
12004        return this.renderer;
12005     },
12006
12007     /**
12008      * Set the defaultUrl used for updates
12009      * @param {String/Function} defaultUrl The url or a function to call to get the url
12010      */
12011     setDefaultUrl : function(defaultUrl){
12012         this.defaultUrl = defaultUrl;
12013     },
12014
12015     /**
12016      * Aborts the executing transaction
12017      */
12018     abort : function(){
12019         if(this.transaction){
12020             Roo.Ajax.abort(this.transaction);
12021         }
12022     },
12023
12024     /**
12025      * Returns true if an update is in progress
12026      * @return {Boolean}
12027      */
12028     isUpdating : function(){
12029         if(this.transaction){
12030             return Roo.Ajax.isLoading(this.transaction);
12031         }
12032         return false;
12033     }
12034 });
12035
12036 /**
12037  * @class Roo.UpdateManager.defaults
12038  * @static (not really - but it helps the doc tool)
12039  * The defaults collection enables customizing the default properties of UpdateManager
12040  */
12041    Roo.UpdateManager.defaults = {
12042        /**
12043          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12044          * @type Number
12045          */
12046          timeout : 30,
12047
12048          /**
12049          * True to process scripts by default (Defaults to false).
12050          * @type Boolean
12051          */
12052         loadScripts : false,
12053
12054         /**
12055         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12056         * @type String
12057         */
12058         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12059         /**
12060          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12061          * @type Boolean
12062          */
12063         disableCaching : false,
12064         /**
12065          * Whether to show indicatorText when loading (Defaults to true).
12066          * @type Boolean
12067          */
12068         showLoadIndicator : true,
12069         /**
12070          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12071          * @type String
12072          */
12073         indicatorText : '<div class="loading-indicator">Loading...</div>'
12074    };
12075
12076 /**
12077  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12078  *Usage:
12079  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12080  * @param {String/HTMLElement/Roo.Element} el The element to update
12081  * @param {String} url The url
12082  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12083  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12084  * @static
12085  * @deprecated
12086  * @member Roo.UpdateManager
12087  */
12088 Roo.UpdateManager.updateElement = function(el, url, params, options){
12089     var um = Roo.get(el, true).getUpdateManager();
12090     Roo.apply(um, options);
12091     um.update(url, params, options ? options.callback : null);
12092 };
12093 // alias for backwards compat
12094 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12095 /**
12096  * @class Roo.UpdateManager.BasicRenderer
12097  * Default Content renderer. Updates the elements innerHTML with the responseText.
12098  */
12099 Roo.UpdateManager.BasicRenderer = function(){};
12100
12101 Roo.UpdateManager.BasicRenderer.prototype = {
12102     /**
12103      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12104      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12105      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12106      * @param {Roo.Element} el The element being rendered
12107      * @param {Object} response The YUI Connect response object
12108      * @param {UpdateManager} updateManager The calling update manager
12109      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12110      */
12111      render : function(el, response, updateManager, callback){
12112         el.update(response.responseText, updateManager.loadScripts, callback);
12113     }
12114 };
12115 /*
12116  * Based on:
12117  * Roo JS
12118  * (c)) Alan Knowles
12119  * Licence : LGPL
12120  */
12121
12122
12123 /**
12124  * @class Roo.DomTemplate
12125  * @extends Roo.Template
12126  * An effort at a dom based template engine..
12127  *
12128  * Similar to XTemplate, except it uses dom parsing to create the template..
12129  *
12130  * Supported features:
12131  *
12132  *  Tags:
12133
12134 <pre><code>
12135       {a_variable} - output encoded.
12136       {a_variable.format:("Y-m-d")} - call a method on the variable
12137       {a_variable:raw} - unencoded output
12138       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12139       {a_variable:this.method_on_template(...)} - call a method on the template object.
12140  
12141 </code></pre>
12142  *  The tpl tag:
12143 <pre><code>
12144         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12145         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12146         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12147         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12148   
12149 </code></pre>
12150  *      
12151  */
12152 Roo.DomTemplate = function()
12153 {
12154      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12155      if (this.html) {
12156         this.compile();
12157      }
12158 };
12159
12160
12161 Roo.extend(Roo.DomTemplate, Roo.Template, {
12162     /**
12163      * id counter for sub templates.
12164      */
12165     id : 0,
12166     /**
12167      * flag to indicate if dom parser is inside a pre,
12168      * it will strip whitespace if not.
12169      */
12170     inPre : false,
12171     
12172     /**
12173      * The various sub templates
12174      */
12175     tpls : false,
12176     
12177     
12178     
12179     /**
12180      *
12181      * basic tag replacing syntax
12182      * WORD:WORD()
12183      *
12184      * // you can fake an object call by doing this
12185      *  x.t:(test,tesT) 
12186      * 
12187      */
12188     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12189     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12190     
12191     iterChild : function (node, method) {
12192         
12193         var oldPre = this.inPre;
12194         if (node.tagName == 'PRE') {
12195             this.inPre = true;
12196         }
12197         for( var i = 0; i < node.childNodes.length; i++) {
12198             method.call(this, node.childNodes[i]);
12199         }
12200         this.inPre = oldPre;
12201     },
12202     
12203     
12204     
12205     /**
12206      * compile the template
12207      *
12208      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12209      *
12210      */
12211     compile: function()
12212     {
12213         var s = this.html;
12214         
12215         // covert the html into DOM...
12216         var doc = false;
12217         var div =false;
12218         try {
12219             doc = document.implementation.createHTMLDocument("");
12220             doc.documentElement.innerHTML =   this.html  ;
12221             div = doc.documentElement;
12222         } catch (e) {
12223             // old IE... - nasty -- it causes all sorts of issues.. with
12224             // images getting pulled from server..
12225             div = document.createElement('div');
12226             div.innerHTML = this.html;
12227         }
12228         //doc.documentElement.innerHTML = htmlBody
12229          
12230         
12231         
12232         this.tpls = [];
12233         var _t = this;
12234         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12235         
12236         var tpls = this.tpls;
12237         
12238         // create a top level template from the snippet..
12239         
12240         //Roo.log(div.innerHTML);
12241         
12242         var tpl = {
12243             uid : 'master',
12244             id : this.id++,
12245             attr : false,
12246             value : false,
12247             body : div.innerHTML,
12248             
12249             forCall : false,
12250             execCall : false,
12251             dom : div,
12252             isTop : true
12253             
12254         };
12255         tpls.unshift(tpl);
12256         
12257         
12258         // compile them...
12259         this.tpls = [];
12260         Roo.each(tpls, function(tp){
12261             this.compileTpl(tp);
12262             this.tpls[tp.id] = tp;
12263         }, this);
12264         
12265         this.master = tpls[0];
12266         return this;
12267         
12268         
12269     },
12270     
12271     compileNode : function(node, istop) {
12272         // test for
12273         //Roo.log(node);
12274         
12275         
12276         // skip anything not a tag..
12277         if (node.nodeType != 1) {
12278             if (node.nodeType == 3 && !this.inPre) {
12279                 // reduce white space..
12280                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12281                 
12282             }
12283             return;
12284         }
12285         
12286         var tpl = {
12287             uid : false,
12288             id : false,
12289             attr : false,
12290             value : false,
12291             body : '',
12292             
12293             forCall : false,
12294             execCall : false,
12295             dom : false,
12296             isTop : istop
12297             
12298             
12299         };
12300         
12301         
12302         switch(true) {
12303             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12304             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12305             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12306             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12307             // no default..
12308         }
12309         
12310         
12311         if (!tpl.attr) {
12312             // just itterate children..
12313             this.iterChild(node,this.compileNode);
12314             return;
12315         }
12316         tpl.uid = this.id++;
12317         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12318         node.removeAttribute('roo-'+ tpl.attr);
12319         if (tpl.attr != 'name') {
12320             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12321             node.parentNode.replaceChild(placeholder,  node);
12322         } else {
12323             
12324             var placeholder =  document.createElement('span');
12325             placeholder.className = 'roo-tpl-' + tpl.value;
12326             node.parentNode.replaceChild(placeholder,  node);
12327         }
12328         
12329         // parent now sees '{domtplXXXX}
12330         this.iterChild(node,this.compileNode);
12331         
12332         // we should now have node body...
12333         var div = document.createElement('div');
12334         div.appendChild(node);
12335         tpl.dom = node;
12336         // this has the unfortunate side effect of converting tagged attributes
12337         // eg. href="{...}" into %7C...%7D
12338         // this has been fixed by searching for those combo's although it's a bit hacky..
12339         
12340         
12341         tpl.body = div.innerHTML;
12342         
12343         
12344          
12345         tpl.id = tpl.uid;
12346         switch(tpl.attr) {
12347             case 'for' :
12348                 switch (tpl.value) {
12349                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12350                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12351                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12352                 }
12353                 break;
12354             
12355             case 'exec':
12356                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12357                 break;
12358             
12359             case 'if':     
12360                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12361                 break;
12362             
12363             case 'name':
12364                 tpl.id  = tpl.value; // replace non characters???
12365                 break;
12366             
12367         }
12368         
12369         
12370         this.tpls.push(tpl);
12371         
12372         
12373         
12374     },
12375     
12376     
12377     
12378     
12379     /**
12380      * Compile a segment of the template into a 'sub-template'
12381      *
12382      * 
12383      * 
12384      *
12385      */
12386     compileTpl : function(tpl)
12387     {
12388         var fm = Roo.util.Format;
12389         var useF = this.disableFormats !== true;
12390         
12391         var sep = Roo.isGecko ? "+\n" : ",\n";
12392         
12393         var undef = function(str) {
12394             Roo.debug && Roo.log("Property not found :"  + str);
12395             return '';
12396         };
12397           
12398         //Roo.log(tpl.body);
12399         
12400         
12401         
12402         var fn = function(m, lbrace, name, format, args)
12403         {
12404             //Roo.log("ARGS");
12405             //Roo.log(arguments);
12406             args = args ? args.replace(/\\'/g,"'") : args;
12407             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12408             if (typeof(format) == 'undefined') {
12409                 format =  'htmlEncode'; 
12410             }
12411             if (format == 'raw' ) {
12412                 format = false;
12413             }
12414             
12415             if(name.substr(0, 6) == 'domtpl'){
12416                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12417             }
12418             
12419             // build an array of options to determine if value is undefined..
12420             
12421             // basically get 'xxxx.yyyy' then do
12422             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12423             //    (function () { Roo.log("Property not found"); return ''; })() :
12424             //    ......
12425             
12426             var udef_ar = [];
12427             var lookfor = '';
12428             Roo.each(name.split('.'), function(st) {
12429                 lookfor += (lookfor.length ? '.': '') + st;
12430                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12431             });
12432             
12433             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12434             
12435             
12436             if(format && useF){
12437                 
12438                 args = args ? ',' + args : "";
12439                  
12440                 if(format.substr(0, 5) != "this."){
12441                     format = "fm." + format + '(';
12442                 }else{
12443                     format = 'this.call("'+ format.substr(5) + '", ';
12444                     args = ", values";
12445                 }
12446                 
12447                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12448             }
12449              
12450             if (args && args.length) {
12451                 // called with xxyx.yuu:(test,test)
12452                 // change to ()
12453                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12454             }
12455             // raw.. - :raw modifier..
12456             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12457             
12458         };
12459         var body;
12460         // branched to use + in gecko and [].join() in others
12461         if(Roo.isGecko){
12462             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12463                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12464                     "';};};";
12465         }else{
12466             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12467             body.push(tpl.body.replace(/(\r\n|\n)/g,
12468                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12469             body.push("'].join('');};};");
12470             body = body.join('');
12471         }
12472         
12473         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12474        
12475         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12476         eval(body);
12477         
12478         return this;
12479     },
12480      
12481     /**
12482      * same as applyTemplate, except it's done to one of the subTemplates
12483      * when using named templates, you can do:
12484      *
12485      * var str = pl.applySubTemplate('your-name', values);
12486      *
12487      * 
12488      * @param {Number} id of the template
12489      * @param {Object} values to apply to template
12490      * @param {Object} parent (normaly the instance of this object)
12491      */
12492     applySubTemplate : function(id, values, parent)
12493     {
12494         
12495         
12496         var t = this.tpls[id];
12497         
12498         
12499         try { 
12500             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12501                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507           
12508             return '';
12509         }
12510         try { 
12511             
12512             if(t.execCall && t.execCall.call(this, values, parent)){
12513                 return '';
12514             }
12515         } catch(e) {
12516             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12517             Roo.log(values);
12518             return '';
12519         }
12520         
12521         try {
12522             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12523             parent = t.target ? values : parent;
12524             if(t.forCall && vs instanceof Array){
12525                 var buf = [];
12526                 for(var i = 0, len = vs.length; i < len; i++){
12527                     try {
12528                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12529                     } catch (e) {
12530                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12531                         Roo.log(e.body);
12532                         //Roo.log(t.compiled);
12533                         Roo.log(vs[i]);
12534                     }   
12535                 }
12536                 return buf.join('');
12537             }
12538         } catch (e) {
12539             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12540             Roo.log(values);
12541             return '';
12542         }
12543         try {
12544             return t.compiled.call(this, vs, parent);
12545         } catch (e) {
12546             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12547             Roo.log(e.body);
12548             //Roo.log(t.compiled);
12549             Roo.log(values);
12550             return '';
12551         }
12552     },
12553
12554    
12555
12556     applyTemplate : function(values){
12557         return this.master.compiled.call(this, values, {});
12558         //var s = this.subs;
12559     },
12560
12561     apply : function(){
12562         return this.applyTemplate.apply(this, arguments);
12563     }
12564
12565  });
12566
12567 Roo.DomTemplate.from = function(el){
12568     el = Roo.getDom(el);
12569     return new Roo.Domtemplate(el.value || el.innerHTML);
12570 };/*
12571  * Based on:
12572  * Ext JS Library 1.1.1
12573  * Copyright(c) 2006-2007, Ext JS, LLC.
12574  *
12575  * Originally Released Under LGPL - original licence link has changed is not relivant.
12576  *
12577  * Fork - LGPL
12578  * <script type="text/javascript">
12579  */
12580
12581 /**
12582  * @class Roo.util.DelayedTask
12583  * Provides a convenient method of performing setTimeout where a new
12584  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12585  * You can use this class to buffer
12586  * the keypress events for a certain number of milliseconds, and perform only if they stop
12587  * for that amount of time.
12588  * @constructor The parameters to this constructor serve as defaults and are not required.
12589  * @param {Function} fn (optional) The default function to timeout
12590  * @param {Object} scope (optional) The default scope of that timeout
12591  * @param {Array} args (optional) The default Array of arguments
12592  */
12593 Roo.util.DelayedTask = function(fn, scope, args){
12594     var id = null, d, t;
12595
12596     var call = function(){
12597         var now = new Date().getTime();
12598         if(now - t >= d){
12599             clearInterval(id);
12600             id = null;
12601             fn.apply(scope, args || []);
12602         }
12603     };
12604     /**
12605      * Cancels any pending timeout and queues a new one
12606      * @param {Number} delay The milliseconds to delay
12607      * @param {Function} newFn (optional) Overrides function passed to constructor
12608      * @param {Object} newScope (optional) Overrides scope passed to constructor
12609      * @param {Array} newArgs (optional) Overrides args passed to constructor
12610      */
12611     this.delay = function(delay, newFn, newScope, newArgs){
12612         if(id && delay != d){
12613             this.cancel();
12614         }
12615         d = delay;
12616         t = new Date().getTime();
12617         fn = newFn || fn;
12618         scope = newScope || scope;
12619         args = newArgs || args;
12620         if(!id){
12621             id = setInterval(call, d);
12622         }
12623     };
12624
12625     /**
12626      * Cancel the last queued timeout
12627      */
12628     this.cancel = function(){
12629         if(id){
12630             clearInterval(id);
12631             id = null;
12632         }
12633     };
12634 };/*
12635  * Based on:
12636  * Ext JS Library 1.1.1
12637  * Copyright(c) 2006-2007, Ext JS, LLC.
12638  *
12639  * Originally Released Under LGPL - original licence link has changed is not relivant.
12640  *
12641  * Fork - LGPL
12642  * <script type="text/javascript">
12643  */
12644  
12645  
12646 Roo.util.TaskRunner = function(interval){
12647     interval = interval || 10;
12648     var tasks = [], removeQueue = [];
12649     var id = 0;
12650     var running = false;
12651
12652     var stopThread = function(){
12653         running = false;
12654         clearInterval(id);
12655         id = 0;
12656     };
12657
12658     var startThread = function(){
12659         if(!running){
12660             running = true;
12661             id = setInterval(runTasks, interval);
12662         }
12663     };
12664
12665     var removeTask = function(task){
12666         removeQueue.push(task);
12667         if(task.onStop){
12668             task.onStop();
12669         }
12670     };
12671
12672     var runTasks = function(){
12673         if(removeQueue.length > 0){
12674             for(var i = 0, len = removeQueue.length; i < len; i++){
12675                 tasks.remove(removeQueue[i]);
12676             }
12677             removeQueue = [];
12678             if(tasks.length < 1){
12679                 stopThread();
12680                 return;
12681             }
12682         }
12683         var now = new Date().getTime();
12684         for(var i = 0, len = tasks.length; i < len; ++i){
12685             var t = tasks[i];
12686             var itime = now - t.taskRunTime;
12687             if(t.interval <= itime){
12688                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12689                 t.taskRunTime = now;
12690                 if(rt === false || t.taskRunCount === t.repeat){
12691                     removeTask(t);
12692                     return;
12693                 }
12694             }
12695             if(t.duration && t.duration <= (now - t.taskStartTime)){
12696                 removeTask(t);
12697             }
12698         }
12699     };
12700
12701     /**
12702      * Queues a new task.
12703      * @param {Object} task
12704      */
12705     this.start = function(task){
12706         tasks.push(task);
12707         task.taskStartTime = new Date().getTime();
12708         task.taskRunTime = 0;
12709         task.taskRunCount = 0;
12710         startThread();
12711         return task;
12712     };
12713
12714     this.stop = function(task){
12715         removeTask(task);
12716         return task;
12717     };
12718
12719     this.stopAll = function(){
12720         stopThread();
12721         for(var i = 0, len = tasks.length; i < len; i++){
12722             if(tasks[i].onStop){
12723                 tasks[i].onStop();
12724             }
12725         }
12726         tasks = [];
12727         removeQueue = [];
12728     };
12729 };
12730
12731 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12732  * Based on:
12733  * Ext JS Library 1.1.1
12734  * Copyright(c) 2006-2007, Ext JS, LLC.
12735  *
12736  * Originally Released Under LGPL - original licence link has changed is not relivant.
12737  *
12738  * Fork - LGPL
12739  * <script type="text/javascript">
12740  */
12741
12742  
12743 /**
12744  * @class Roo.util.MixedCollection
12745  * @extends Roo.util.Observable
12746  * A Collection class that maintains both numeric indexes and keys and exposes events.
12747  * @constructor
12748  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12749  * collection (defaults to false)
12750  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12751  * and return the key value for that item.  This is used when available to look up the key on items that
12752  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12753  * equivalent to providing an implementation for the {@link #getKey} method.
12754  */
12755 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12756     this.items = [];
12757     this.map = {};
12758     this.keys = [];
12759     this.length = 0;
12760     this.addEvents({
12761         /**
12762          * @event clear
12763          * Fires when the collection is cleared.
12764          */
12765         "clear" : true,
12766         /**
12767          * @event add
12768          * Fires when an item is added to the collection.
12769          * @param {Number} index The index at which the item was added.
12770          * @param {Object} o The item added.
12771          * @param {String} key The key associated with the added item.
12772          */
12773         "add" : true,
12774         /**
12775          * @event replace
12776          * Fires when an item is replaced in the collection.
12777          * @param {String} key he key associated with the new added.
12778          * @param {Object} old The item being replaced.
12779          * @param {Object} new The new item.
12780          */
12781         "replace" : true,
12782         /**
12783          * @event remove
12784          * Fires when an item is removed from the collection.
12785          * @param {Object} o The item being removed.
12786          * @param {String} key (optional) The key associated with the removed item.
12787          */
12788         "remove" : true,
12789         "sort" : true
12790     });
12791     this.allowFunctions = allowFunctions === true;
12792     if(keyFn){
12793         this.getKey = keyFn;
12794     }
12795     Roo.util.MixedCollection.superclass.constructor.call(this);
12796 };
12797
12798 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12799     allowFunctions : false,
12800     
12801 /**
12802  * Adds an item to the collection.
12803  * @param {String} key The key to associate with the item
12804  * @param {Object} o The item to add.
12805  * @return {Object} The item added.
12806  */
12807     add : function(key, o){
12808         if(arguments.length == 1){
12809             o = arguments[0];
12810             key = this.getKey(o);
12811         }
12812         if(typeof key == "undefined" || key === null){
12813             this.length++;
12814             this.items.push(o);
12815             this.keys.push(null);
12816         }else{
12817             var old = this.map[key];
12818             if(old){
12819                 return this.replace(key, o);
12820             }
12821             this.length++;
12822             this.items.push(o);
12823             this.map[key] = o;
12824             this.keys.push(key);
12825         }
12826         this.fireEvent("add", this.length-1, o, key);
12827         return o;
12828     },
12829        
12830 /**
12831   * MixedCollection has a generic way to fetch keys if you implement getKey.
12832 <pre><code>
12833 // normal way
12834 var mc = new Roo.util.MixedCollection();
12835 mc.add(someEl.dom.id, someEl);
12836 mc.add(otherEl.dom.id, otherEl);
12837 //and so on
12838
12839 // using getKey
12840 var mc = new Roo.util.MixedCollection();
12841 mc.getKey = function(el){
12842    return el.dom.id;
12843 };
12844 mc.add(someEl);
12845 mc.add(otherEl);
12846
12847 // or via the constructor
12848 var mc = new Roo.util.MixedCollection(false, function(el){
12849    return el.dom.id;
12850 });
12851 mc.add(someEl);
12852 mc.add(otherEl);
12853 </code></pre>
12854  * @param o {Object} The item for which to find the key.
12855  * @return {Object} The key for the passed item.
12856  */
12857     getKey : function(o){
12858          return o.id; 
12859     },
12860    
12861 /**
12862  * Replaces an item in the collection.
12863  * @param {String} key The key associated with the item to replace, or the item to replace.
12864  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12865  * @return {Object}  The new item.
12866  */
12867     replace : function(key, o){
12868         if(arguments.length == 1){
12869             o = arguments[0];
12870             key = this.getKey(o);
12871         }
12872         var old = this.item(key);
12873         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12874              return this.add(key, o);
12875         }
12876         var index = this.indexOfKey(key);
12877         this.items[index] = o;
12878         this.map[key] = o;
12879         this.fireEvent("replace", key, old, o);
12880         return o;
12881     },
12882    
12883 /**
12884  * Adds all elements of an Array or an Object to the collection.
12885  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12886  * an Array of values, each of which are added to the collection.
12887  */
12888     addAll : function(objs){
12889         if(arguments.length > 1 || objs instanceof Array){
12890             var args = arguments.length > 1 ? arguments : objs;
12891             for(var i = 0, len = args.length; i < len; i++){
12892                 this.add(args[i]);
12893             }
12894         }else{
12895             for(var key in objs){
12896                 if(this.allowFunctions || typeof objs[key] != "function"){
12897                     this.add(key, objs[key]);
12898                 }
12899             }
12900         }
12901     },
12902    
12903 /**
12904  * Executes the specified function once for every item in the collection, passing each
12905  * item as the first and only parameter. returning false from the function will stop the iteration.
12906  * @param {Function} fn The function to execute for each item.
12907  * @param {Object} scope (optional) The scope in which to execute the function.
12908  */
12909     each : function(fn, scope){
12910         var items = [].concat(this.items); // each safe for removal
12911         for(var i = 0, len = items.length; i < len; i++){
12912             if(fn.call(scope || items[i], items[i], i, len) === false){
12913                 break;
12914             }
12915         }
12916     },
12917    
12918 /**
12919  * Executes the specified function once for every key in the collection, passing each
12920  * key, and its associated item as the first two parameters.
12921  * @param {Function} fn The function to execute for each item.
12922  * @param {Object} scope (optional) The scope in which to execute the function.
12923  */
12924     eachKey : function(fn, scope){
12925         for(var i = 0, len = this.keys.length; i < len; i++){
12926             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12927         }
12928     },
12929    
12930 /**
12931  * Returns the first item in the collection which elicits a true return value from the
12932  * passed selection function.
12933  * @param {Function} fn The selection function to execute for each item.
12934  * @param {Object} scope (optional) The scope in which to execute the function.
12935  * @return {Object} The first item in the collection which returned true from the selection function.
12936  */
12937     find : function(fn, scope){
12938         for(var i = 0, len = this.items.length; i < len; i++){
12939             if(fn.call(scope || window, this.items[i], this.keys[i])){
12940                 return this.items[i];
12941             }
12942         }
12943         return null;
12944     },
12945    
12946 /**
12947  * Inserts an item at the specified index in the collection.
12948  * @param {Number} index The index to insert the item at.
12949  * @param {String} key The key to associate with the new item, or the item itself.
12950  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12951  * @return {Object} The item inserted.
12952  */
12953     insert : function(index, key, o){
12954         if(arguments.length == 2){
12955             o = arguments[1];
12956             key = this.getKey(o);
12957         }
12958         if(index >= this.length){
12959             return this.add(key, o);
12960         }
12961         this.length++;
12962         this.items.splice(index, 0, o);
12963         if(typeof key != "undefined" && key != null){
12964             this.map[key] = o;
12965         }
12966         this.keys.splice(index, 0, key);
12967         this.fireEvent("add", index, o, key);
12968         return o;
12969     },
12970    
12971 /**
12972  * Removed an item from the collection.
12973  * @param {Object} o The item to remove.
12974  * @return {Object} The item removed.
12975  */
12976     remove : function(o){
12977         return this.removeAt(this.indexOf(o));
12978     },
12979    
12980 /**
12981  * Remove an item from a specified index in the collection.
12982  * @param {Number} index The index within the collection of the item to remove.
12983  */
12984     removeAt : function(index){
12985         if(index < this.length && index >= 0){
12986             this.length--;
12987             var o = this.items[index];
12988             this.items.splice(index, 1);
12989             var key = this.keys[index];
12990             if(typeof key != "undefined"){
12991                 delete this.map[key];
12992             }
12993             this.keys.splice(index, 1);
12994             this.fireEvent("remove", o, key);
12995         }
12996     },
12997    
12998 /**
12999  * Removed an item associated with the passed key fom the collection.
13000  * @param {String} key The key of the item to remove.
13001  */
13002     removeKey : function(key){
13003         return this.removeAt(this.indexOfKey(key));
13004     },
13005    
13006 /**
13007  * Returns the number of items in the collection.
13008  * @return {Number} the number of items in the collection.
13009  */
13010     getCount : function(){
13011         return this.length; 
13012     },
13013    
13014 /**
13015  * Returns index within the collection of the passed Object.
13016  * @param {Object} o The item to find the index of.
13017  * @return {Number} index of the item.
13018  */
13019     indexOf : function(o){
13020         if(!this.items.indexOf){
13021             for(var i = 0, len = this.items.length; i < len; i++){
13022                 if(this.items[i] == o) return i;
13023             }
13024             return -1;
13025         }else{
13026             return this.items.indexOf(o);
13027         }
13028     },
13029    
13030 /**
13031  * Returns index within the collection of the passed key.
13032  * @param {String} key The key to find the index of.
13033  * @return {Number} index of the key.
13034  */
13035     indexOfKey : function(key){
13036         if(!this.keys.indexOf){
13037             for(var i = 0, len = this.keys.length; i < len; i++){
13038                 if(this.keys[i] == key) return i;
13039             }
13040             return -1;
13041         }else{
13042             return this.keys.indexOf(key);
13043         }
13044     },
13045    
13046 /**
13047  * Returns the item associated with the passed key OR index. Key has priority over index.
13048  * @param {String/Number} key The key or index of the item.
13049  * @return {Object} The item associated with the passed key.
13050  */
13051     item : function(key){
13052         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13053         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13054     },
13055     
13056 /**
13057  * Returns the item at the specified index.
13058  * @param {Number} index The index of the item.
13059  * @return {Object}
13060  */
13061     itemAt : function(index){
13062         return this.items[index];
13063     },
13064     
13065 /**
13066  * Returns the item associated with the passed key.
13067  * @param {String/Number} key The key of the item.
13068  * @return {Object} The item associated with the passed key.
13069  */
13070     key : function(key){
13071         return this.map[key];
13072     },
13073    
13074 /**
13075  * Returns true if the collection contains the passed Object as an item.
13076  * @param {Object} o  The Object to look for in the collection.
13077  * @return {Boolean} True if the collection contains the Object as an item.
13078  */
13079     contains : function(o){
13080         return this.indexOf(o) != -1;
13081     },
13082    
13083 /**
13084  * Returns true if the collection contains the passed Object as a key.
13085  * @param {String} key The key to look for in the collection.
13086  * @return {Boolean} True if the collection contains the Object as a key.
13087  */
13088     containsKey : function(key){
13089         return typeof this.map[key] != "undefined";
13090     },
13091    
13092 /**
13093  * Removes all items from the collection.
13094  */
13095     clear : function(){
13096         this.length = 0;
13097         this.items = [];
13098         this.keys = [];
13099         this.map = {};
13100         this.fireEvent("clear");
13101     },
13102    
13103 /**
13104  * Returns the first item in the collection.
13105  * @return {Object} the first item in the collection..
13106  */
13107     first : function(){
13108         return this.items[0]; 
13109     },
13110    
13111 /**
13112  * Returns the last item in the collection.
13113  * @return {Object} the last item in the collection..
13114  */
13115     last : function(){
13116         return this.items[this.length-1];   
13117     },
13118     
13119     _sort : function(property, dir, fn){
13120         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13121         fn = fn || function(a, b){
13122             return a-b;
13123         };
13124         var c = [], k = this.keys, items = this.items;
13125         for(var i = 0, len = items.length; i < len; i++){
13126             c[c.length] = {key: k[i], value: items[i], index: i};
13127         }
13128         c.sort(function(a, b){
13129             var v = fn(a[property], b[property]) * dsc;
13130             if(v == 0){
13131                 v = (a.index < b.index ? -1 : 1);
13132             }
13133             return v;
13134         });
13135         for(var i = 0, len = c.length; i < len; i++){
13136             items[i] = c[i].value;
13137             k[i] = c[i].key;
13138         }
13139         this.fireEvent("sort", this);
13140     },
13141     
13142     /**
13143      * Sorts this collection with the passed comparison function
13144      * @param {String} direction (optional) "ASC" or "DESC"
13145      * @param {Function} fn (optional) comparison function
13146      */
13147     sort : function(dir, fn){
13148         this._sort("value", dir, fn);
13149     },
13150     
13151     /**
13152      * Sorts this collection by keys
13153      * @param {String} direction (optional) "ASC" or "DESC"
13154      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13155      */
13156     keySort : function(dir, fn){
13157         this._sort("key", dir, fn || function(a, b){
13158             return String(a).toUpperCase()-String(b).toUpperCase();
13159         });
13160     },
13161     
13162     /**
13163      * Returns a range of items in this collection
13164      * @param {Number} startIndex (optional) defaults to 0
13165      * @param {Number} endIndex (optional) default to the last item
13166      * @return {Array} An array of items
13167      */
13168     getRange : function(start, end){
13169         var items = this.items;
13170         if(items.length < 1){
13171             return [];
13172         }
13173         start = start || 0;
13174         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13175         var r = [];
13176         if(start <= end){
13177             for(var i = start; i <= end; i++) {
13178                     r[r.length] = items[i];
13179             }
13180         }else{
13181             for(var i = start; i >= end; i--) {
13182                     r[r.length] = items[i];
13183             }
13184         }
13185         return r;
13186     },
13187         
13188     /**
13189      * Filter the <i>objects</i> in this collection by a specific property. 
13190      * Returns a new collection that has been filtered.
13191      * @param {String} property A property on your objects
13192      * @param {String/RegExp} value Either string that the property values 
13193      * should start with or a RegExp to test against the property
13194      * @return {MixedCollection} The new filtered collection
13195      */
13196     filter : function(property, value){
13197         if(!value.exec){ // not a regex
13198             value = String(value);
13199             if(value.length == 0){
13200                 return this.clone();
13201             }
13202             value = new RegExp("^" + Roo.escapeRe(value), "i");
13203         }
13204         return this.filterBy(function(o){
13205             return o && value.test(o[property]);
13206         });
13207         },
13208     
13209     /**
13210      * Filter by a function. * Returns a new collection that has been filtered.
13211      * The passed function will be called with each 
13212      * object in the collection. If the function returns true, the value is included 
13213      * otherwise it is filtered.
13214      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13215      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13216      * @return {MixedCollection} The new filtered collection
13217      */
13218     filterBy : function(fn, scope){
13219         var r = new Roo.util.MixedCollection();
13220         r.getKey = this.getKey;
13221         var k = this.keys, it = this.items;
13222         for(var i = 0, len = it.length; i < len; i++){
13223             if(fn.call(scope||this, it[i], k[i])){
13224                                 r.add(k[i], it[i]);
13225                         }
13226         }
13227         return r;
13228     },
13229     
13230     /**
13231      * Creates a duplicate of this collection
13232      * @return {MixedCollection}
13233      */
13234     clone : function(){
13235         var r = new Roo.util.MixedCollection();
13236         var k = this.keys, it = this.items;
13237         for(var i = 0, len = it.length; i < len; i++){
13238             r.add(k[i], it[i]);
13239         }
13240         r.getKey = this.getKey;
13241         return r;
13242     }
13243 });
13244 /**
13245  * Returns the item associated with the passed key or index.
13246  * @method
13247  * @param {String/Number} key The key or index of the item.
13248  * @return {Object} The item associated with the passed key.
13249  */
13250 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13251  * Based on:
13252  * Ext JS Library 1.1.1
13253  * Copyright(c) 2006-2007, Ext JS, LLC.
13254  *
13255  * Originally Released Under LGPL - original licence link has changed is not relivant.
13256  *
13257  * Fork - LGPL
13258  * <script type="text/javascript">
13259  */
13260 /**
13261  * @class Roo.util.JSON
13262  * Modified version of Douglas Crockford"s json.js that doesn"t
13263  * mess with the Object prototype 
13264  * http://www.json.org/js.html
13265  * @singleton
13266  */
13267 Roo.util.JSON = new (function(){
13268     var useHasOwn = {}.hasOwnProperty ? true : false;
13269     
13270     // crashes Safari in some instances
13271     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13272     
13273     var pad = function(n) {
13274         return n < 10 ? "0" + n : n;
13275     };
13276     
13277     var m = {
13278         "\b": '\\b',
13279         "\t": '\\t',
13280         "\n": '\\n',
13281         "\f": '\\f',
13282         "\r": '\\r',
13283         '"' : '\\"',
13284         "\\": '\\\\'
13285     };
13286
13287     var encodeString = function(s){
13288         if (/["\\\x00-\x1f]/.test(s)) {
13289             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13290                 var c = m[b];
13291                 if(c){
13292                     return c;
13293                 }
13294                 c = b.charCodeAt();
13295                 return "\\u00" +
13296                     Math.floor(c / 16).toString(16) +
13297                     (c % 16).toString(16);
13298             }) + '"';
13299         }
13300         return '"' + s + '"';
13301     };
13302     
13303     var encodeArray = function(o){
13304         var a = ["["], b, i, l = o.length, v;
13305             for (i = 0; i < l; i += 1) {
13306                 v = o[i];
13307                 switch (typeof v) {
13308                     case "undefined":
13309                     case "function":
13310                     case "unknown":
13311                         break;
13312                     default:
13313                         if (b) {
13314                             a.push(',');
13315                         }
13316                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13317                         b = true;
13318                 }
13319             }
13320             a.push("]");
13321             return a.join("");
13322     };
13323     
13324     var encodeDate = function(o){
13325         return '"' + o.getFullYear() + "-" +
13326                 pad(o.getMonth() + 1) + "-" +
13327                 pad(o.getDate()) + "T" +
13328                 pad(o.getHours()) + ":" +
13329                 pad(o.getMinutes()) + ":" +
13330                 pad(o.getSeconds()) + '"';
13331     };
13332     
13333     /**
13334      * Encodes an Object, Array or other value
13335      * @param {Mixed} o The variable to encode
13336      * @return {String} The JSON string
13337      */
13338     this.encode = function(o)
13339     {
13340         // should this be extended to fully wrap stringify..
13341         
13342         if(typeof o == "undefined" || o === null){
13343             return "null";
13344         }else if(o instanceof Array){
13345             return encodeArray(o);
13346         }else if(o instanceof Date){
13347             return encodeDate(o);
13348         }else if(typeof o == "string"){
13349             return encodeString(o);
13350         }else if(typeof o == "number"){
13351             return isFinite(o) ? String(o) : "null";
13352         }else if(typeof o == "boolean"){
13353             return String(o);
13354         }else {
13355             var a = ["{"], b, i, v;
13356             for (i in o) {
13357                 if(!useHasOwn || o.hasOwnProperty(i)) {
13358                     v = o[i];
13359                     switch (typeof v) {
13360                     case "undefined":
13361                     case "function":
13362                     case "unknown":
13363                         break;
13364                     default:
13365                         if(b){
13366                             a.push(',');
13367                         }
13368                         a.push(this.encode(i), ":",
13369                                 v === null ? "null" : this.encode(v));
13370                         b = true;
13371                     }
13372                 }
13373             }
13374             a.push("}");
13375             return a.join("");
13376         }
13377     };
13378     
13379     /**
13380      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13381      * @param {String} json The JSON string
13382      * @return {Object} The resulting object
13383      */
13384     this.decode = function(json){
13385         
13386         return  /** eval:var:json */ eval("(" + json + ')');
13387     };
13388 })();
13389 /** 
13390  * Shorthand for {@link Roo.util.JSON#encode}
13391  * @member Roo encode 
13392  * @method */
13393 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13394 /** 
13395  * Shorthand for {@link Roo.util.JSON#decode}
13396  * @member Roo decode 
13397  * @method */
13398 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13399 /*
13400  * Based on:
13401  * Ext JS Library 1.1.1
13402  * Copyright(c) 2006-2007, Ext JS, LLC.
13403  *
13404  * Originally Released Under LGPL - original licence link has changed is not relivant.
13405  *
13406  * Fork - LGPL
13407  * <script type="text/javascript">
13408  */
13409  
13410 /**
13411  * @class Roo.util.Format
13412  * Reusable data formatting functions
13413  * @singleton
13414  */
13415 Roo.util.Format = function(){
13416     var trimRe = /^\s+|\s+$/g;
13417     return {
13418         /**
13419          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13420          * @param {String} value The string to truncate
13421          * @param {Number} length The maximum length to allow before truncating
13422          * @return {String} The converted text
13423          */
13424         ellipsis : function(value, len){
13425             if(value && value.length > len){
13426                 return value.substr(0, len-3)+"...";
13427             }
13428             return value;
13429         },
13430
13431         /**
13432          * Checks a reference and converts it to empty string if it is undefined
13433          * @param {Mixed} value Reference to check
13434          * @return {Mixed} Empty string if converted, otherwise the original value
13435          */
13436         undef : function(value){
13437             return typeof value != "undefined" ? value : "";
13438         },
13439
13440         /**
13441          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13442          * @param {String} value The string to encode
13443          * @return {String} The encoded text
13444          */
13445         htmlEncode : function(value){
13446             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13447         },
13448
13449         /**
13450          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13451          * @param {String} value The string to decode
13452          * @return {String} The decoded text
13453          */
13454         htmlDecode : function(value){
13455             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13456         },
13457
13458         /**
13459          * Trims any whitespace from either side of a string
13460          * @param {String} value The text to trim
13461          * @return {String} The trimmed text
13462          */
13463         trim : function(value){
13464             return String(value).replace(trimRe, "");
13465         },
13466
13467         /**
13468          * Returns a substring from within an original string
13469          * @param {String} value The original text
13470          * @param {Number} start The start index of the substring
13471          * @param {Number} length The length of the substring
13472          * @return {String} The substring
13473          */
13474         substr : function(value, start, length){
13475             return String(value).substr(start, length);
13476         },
13477
13478         /**
13479          * Converts a string to all lower case letters
13480          * @param {String} value The text to convert
13481          * @return {String} The converted text
13482          */
13483         lowercase : function(value){
13484             return String(value).toLowerCase();
13485         },
13486
13487         /**
13488          * Converts a string to all upper case letters
13489          * @param {String} value The text to convert
13490          * @return {String} The converted text
13491          */
13492         uppercase : function(value){
13493             return String(value).toUpperCase();
13494         },
13495
13496         /**
13497          * Converts the first character only of a string to upper case
13498          * @param {String} value The text to convert
13499          * @return {String} The converted text
13500          */
13501         capitalize : function(value){
13502             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13503         },
13504
13505         // private
13506         call : function(value, fn){
13507             if(arguments.length > 2){
13508                 var args = Array.prototype.slice.call(arguments, 2);
13509                 args.unshift(value);
13510                  
13511                 return /** eval:var:value */  eval(fn).apply(window, args);
13512             }else{
13513                 /** eval:var:value */
13514                 return /** eval:var:value */ eval(fn).call(window, value);
13515             }
13516         },
13517
13518        
13519         /**
13520          * safer version of Math.toFixed..??/
13521          * @param {Number/String} value The numeric value to format
13522          * @param {Number/String} value Decimal places 
13523          * @return {String} The formatted currency string
13524          */
13525         toFixed : function(v, n)
13526         {
13527             // why not use to fixed - precision is buggered???
13528             if (!n) {
13529                 return Math.round(v-0);
13530             }
13531             var fact = Math.pow(10,n+1);
13532             v = (Math.round((v-0)*fact))/fact;
13533             var z = (''+fact).substring(2);
13534             if (v == Math.floor(v)) {
13535                 return Math.floor(v) + '.' + z;
13536             }
13537             
13538             // now just padd decimals..
13539             var ps = String(v).split('.');
13540             var fd = (ps[1] + z);
13541             var r = fd.substring(0,n); 
13542             var rm = fd.substring(n); 
13543             if (rm < 5) {
13544                 return ps[0] + '.' + r;
13545             }
13546             r*=1; // turn it into a number;
13547             r++;
13548             if (String(r).length != n) {
13549                 ps[0]*=1;
13550                 ps[0]++;
13551                 r = String(r).substring(1); // chop the end off.
13552             }
13553             
13554             return ps[0] + '.' + r;
13555              
13556         },
13557         
13558         /**
13559          * Format a number as US currency
13560          * @param {Number/String} value The numeric value to format
13561          * @return {String} The formatted currency string
13562          */
13563         usMoney : function(v){
13564             return '$' + Roo.util.Format.number(v);
13565         },
13566         
13567         /**
13568          * Format a number
13569          * eventually this should probably emulate php's number_format
13570          * @param {Number/String} value The numeric value to format
13571          * @param {Number} decimals number of decimal places
13572          * @return {String} The formatted currency string
13573          */
13574         number : function(v,decimals)
13575         {
13576             // multiply and round.
13577             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13578             var mul = Math.pow(10, decimals);
13579             var zero = String(mul).substring(1);
13580             v = (Math.round((v-0)*mul))/mul;
13581             
13582             // if it's '0' number.. then
13583             
13584             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13585             v = String(v);
13586             var ps = v.split('.');
13587             var whole = ps[0];
13588             
13589             
13590             var r = /(\d+)(\d{3})/;
13591             // add comma's
13592             while (r.test(whole)) {
13593                 whole = whole.replace(r, '$1' + ',' + '$2');
13594             }
13595             
13596             
13597             var sub = ps[1] ?
13598                     // has decimals..
13599                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13600                     // does not have decimals
13601                     (decimals ? ('.' + zero) : '');
13602             
13603             
13604             return whole + sub ;
13605         },
13606         
13607         /**
13608          * Parse a value into a formatted date using the specified format pattern.
13609          * @param {Mixed} value The value to format
13610          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13611          * @return {String} The formatted date string
13612          */
13613         date : function(v, format){
13614             if(!v){
13615                 return "";
13616             }
13617             if(!(v instanceof Date)){
13618                 v = new Date(Date.parse(v));
13619             }
13620             return v.dateFormat(format || Roo.util.Format.defaults.date);
13621         },
13622
13623         /**
13624          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13625          * @param {String} format Any valid date format string
13626          * @return {Function} The date formatting function
13627          */
13628         dateRenderer : function(format){
13629             return function(v){
13630                 return Roo.util.Format.date(v, format);  
13631             };
13632         },
13633
13634         // private
13635         stripTagsRE : /<\/?[^>]+>/gi,
13636         
13637         /**
13638          * Strips all HTML tags
13639          * @param {Mixed} value The text from which to strip tags
13640          * @return {String} The stripped text
13641          */
13642         stripTags : function(v){
13643             return !v ? v : String(v).replace(this.stripTagsRE, "");
13644         }
13645     };
13646 }();
13647 Roo.util.Format.defaults = {
13648     date : 'd/M/Y'
13649 };/*
13650  * Based on:
13651  * Ext JS Library 1.1.1
13652  * Copyright(c) 2006-2007, Ext JS, LLC.
13653  *
13654  * Originally Released Under LGPL - original licence link has changed is not relivant.
13655  *
13656  * Fork - LGPL
13657  * <script type="text/javascript">
13658  */
13659
13660
13661  
13662
13663 /**
13664  * @class Roo.MasterTemplate
13665  * @extends Roo.Template
13666  * Provides a template that can have child templates. The syntax is:
13667 <pre><code>
13668 var t = new Roo.MasterTemplate(
13669         '&lt;select name="{name}"&gt;',
13670                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13671         '&lt;/select&gt;'
13672 );
13673 t.add('options', {value: 'foo', text: 'bar'});
13674 // or you can add multiple child elements in one shot
13675 t.addAll('options', [
13676     {value: 'foo', text: 'bar'},
13677     {value: 'foo2', text: 'bar2'},
13678     {value: 'foo3', text: 'bar3'}
13679 ]);
13680 // then append, applying the master template values
13681 t.append('my-form', {name: 'my-select'});
13682 </code></pre>
13683 * A name attribute for the child template is not required if you have only one child
13684 * template or you want to refer to them by index.
13685  */
13686 Roo.MasterTemplate = function(){
13687     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13688     this.originalHtml = this.html;
13689     var st = {};
13690     var m, re = this.subTemplateRe;
13691     re.lastIndex = 0;
13692     var subIndex = 0;
13693     while(m = re.exec(this.html)){
13694         var name = m[1], content = m[2];
13695         st[subIndex] = {
13696             name: name,
13697             index: subIndex,
13698             buffer: [],
13699             tpl : new Roo.Template(content)
13700         };
13701         if(name){
13702             st[name] = st[subIndex];
13703         }
13704         st[subIndex].tpl.compile();
13705         st[subIndex].tpl.call = this.call.createDelegate(this);
13706         subIndex++;
13707     }
13708     this.subCount = subIndex;
13709     this.subs = st;
13710 };
13711 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13712     /**
13713     * The regular expression used to match sub templates
13714     * @type RegExp
13715     * @property
13716     */
13717     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13718
13719     /**
13720      * Applies the passed values to a child template.
13721      * @param {String/Number} name (optional) The name or index of the child template
13722      * @param {Array/Object} values The values to be applied to the template
13723      * @return {MasterTemplate} this
13724      */
13725      add : function(name, values){
13726         if(arguments.length == 1){
13727             values = arguments[0];
13728             name = 0;
13729         }
13730         var s = this.subs[name];
13731         s.buffer[s.buffer.length] = s.tpl.apply(values);
13732         return this;
13733     },
13734
13735     /**
13736      * Applies all the passed values to a child template.
13737      * @param {String/Number} name (optional) The name or index of the child template
13738      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13739      * @param {Boolean} reset (optional) True to reset the template first
13740      * @return {MasterTemplate} this
13741      */
13742     fill : function(name, values, reset){
13743         var a = arguments;
13744         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13745             values = a[0];
13746             name = 0;
13747             reset = a[1];
13748         }
13749         if(reset){
13750             this.reset();
13751         }
13752         for(var i = 0, len = values.length; i < len; i++){
13753             this.add(name, values[i]);
13754         }
13755         return this;
13756     },
13757
13758     /**
13759      * Resets the template for reuse
13760      * @return {MasterTemplate} this
13761      */
13762      reset : function(){
13763         var s = this.subs;
13764         for(var i = 0; i < this.subCount; i++){
13765             s[i].buffer = [];
13766         }
13767         return this;
13768     },
13769
13770     applyTemplate : function(values){
13771         var s = this.subs;
13772         var replaceIndex = -1;
13773         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13774             return s[++replaceIndex].buffer.join("");
13775         });
13776         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13777     },
13778
13779     apply : function(){
13780         return this.applyTemplate.apply(this, arguments);
13781     },
13782
13783     compile : function(){return this;}
13784 });
13785
13786 /**
13787  * Alias for fill().
13788  * @method
13789  */
13790 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13791  /**
13792  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13793  * var tpl = Roo.MasterTemplate.from('element-id');
13794  * @param {String/HTMLElement} el
13795  * @param {Object} config
13796  * @static
13797  */
13798 Roo.MasterTemplate.from = function(el, config){
13799     el = Roo.getDom(el);
13800     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13801 };/*
13802  * Based on:
13803  * Ext JS Library 1.1.1
13804  * Copyright(c) 2006-2007, Ext JS, LLC.
13805  *
13806  * Originally Released Under LGPL - original licence link has changed is not relivant.
13807  *
13808  * Fork - LGPL
13809  * <script type="text/javascript">
13810  */
13811
13812  
13813 /**
13814  * @class Roo.util.CSS
13815  * Utility class for manipulating CSS rules
13816  * @singleton
13817  */
13818 Roo.util.CSS = function(){
13819         var rules = null;
13820         var doc = document;
13821
13822     var camelRe = /(-[a-z])/gi;
13823     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13824
13825    return {
13826    /**
13827     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13828     * tag and appended to the HEAD of the document.
13829     * @param {String|Object} cssText The text containing the css rules
13830     * @param {String} id An id to add to the stylesheet for later removal
13831     * @return {StyleSheet}
13832     */
13833     createStyleSheet : function(cssText, id){
13834         var ss;
13835         var head = doc.getElementsByTagName("head")[0];
13836         var nrules = doc.createElement("style");
13837         nrules.setAttribute("type", "text/css");
13838         if(id){
13839             nrules.setAttribute("id", id);
13840         }
13841         if (typeof(cssText) != 'string') {
13842             // support object maps..
13843             // not sure if this a good idea.. 
13844             // perhaps it should be merged with the general css handling
13845             // and handle js style props.
13846             var cssTextNew = [];
13847             for(var n in cssText) {
13848                 var citems = [];
13849                 for(var k in cssText[n]) {
13850                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13851                 }
13852                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13853                 
13854             }
13855             cssText = cssTextNew.join("\n");
13856             
13857         }
13858        
13859        
13860        if(Roo.isIE){
13861            head.appendChild(nrules);
13862            ss = nrules.styleSheet;
13863            ss.cssText = cssText;
13864        }else{
13865            try{
13866                 nrules.appendChild(doc.createTextNode(cssText));
13867            }catch(e){
13868                nrules.cssText = cssText; 
13869            }
13870            head.appendChild(nrules);
13871            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13872        }
13873        this.cacheStyleSheet(ss);
13874        return ss;
13875    },
13876
13877    /**
13878     * Removes a style or link tag by id
13879     * @param {String} id The id of the tag
13880     */
13881    removeStyleSheet : function(id){
13882        var existing = doc.getElementById(id);
13883        if(existing){
13884            existing.parentNode.removeChild(existing);
13885        }
13886    },
13887
13888    /**
13889     * Dynamically swaps an existing stylesheet reference for a new one
13890     * @param {String} id The id of an existing link tag to remove
13891     * @param {String} url The href of the new stylesheet to include
13892     */
13893    swapStyleSheet : function(id, url){
13894        this.removeStyleSheet(id);
13895        var ss = doc.createElement("link");
13896        ss.setAttribute("rel", "stylesheet");
13897        ss.setAttribute("type", "text/css");
13898        ss.setAttribute("id", id);
13899        ss.setAttribute("href", url);
13900        doc.getElementsByTagName("head")[0].appendChild(ss);
13901    },
13902    
13903    /**
13904     * Refresh the rule cache if you have dynamically added stylesheets
13905     * @return {Object} An object (hash) of rules indexed by selector
13906     */
13907    refreshCache : function(){
13908        return this.getRules(true);
13909    },
13910
13911    // private
13912    cacheStyleSheet : function(stylesheet){
13913        if(!rules){
13914            rules = {};
13915        }
13916        try{// try catch for cross domain access issue
13917            var ssRules = stylesheet.cssRules || stylesheet.rules;
13918            for(var j = ssRules.length-1; j >= 0; --j){
13919                rules[ssRules[j].selectorText] = ssRules[j];
13920            }
13921        }catch(e){}
13922    },
13923    
13924    /**
13925     * Gets all css rules for the document
13926     * @param {Boolean} refreshCache true to refresh the internal cache
13927     * @return {Object} An object (hash) of rules indexed by selector
13928     */
13929    getRules : function(refreshCache){
13930                 if(rules == null || refreshCache){
13931                         rules = {};
13932                         var ds = doc.styleSheets;
13933                         for(var i =0, len = ds.length; i < len; i++){
13934                             try{
13935                         this.cacheStyleSheet(ds[i]);
13936                     }catch(e){} 
13937                 }
13938                 }
13939                 return rules;
13940         },
13941         
13942         /**
13943     * Gets an an individual CSS rule by selector(s)
13944     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13945     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13946     * @return {CSSRule} The CSS rule or null if one is not found
13947     */
13948    getRule : function(selector, refreshCache){
13949                 var rs = this.getRules(refreshCache);
13950                 if(!(selector instanceof Array)){
13951                     return rs[selector];
13952                 }
13953                 for(var i = 0; i < selector.length; i++){
13954                         if(rs[selector[i]]){
13955                                 return rs[selector[i]];
13956                         }
13957                 }
13958                 return null;
13959         },
13960         
13961         
13962         /**
13963     * Updates a rule property
13964     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13965     * @param {String} property The css property
13966     * @param {String} value The new value for the property
13967     * @return {Boolean} true If a rule was found and updated
13968     */
13969    updateRule : function(selector, property, value){
13970                 if(!(selector instanceof Array)){
13971                         var rule = this.getRule(selector);
13972                         if(rule){
13973                                 rule.style[property.replace(camelRe, camelFn)] = value;
13974                                 return true;
13975                         }
13976                 }else{
13977                         for(var i = 0; i < selector.length; i++){
13978                                 if(this.updateRule(selector[i], property, value)){
13979                                         return true;
13980                                 }
13981                         }
13982                 }
13983                 return false;
13984         }
13985    };   
13986 }();/*
13987  * Based on:
13988  * Ext JS Library 1.1.1
13989  * Copyright(c) 2006-2007, Ext JS, LLC.
13990  *
13991  * Originally Released Under LGPL - original licence link has changed is not relivant.
13992  *
13993  * Fork - LGPL
13994  * <script type="text/javascript">
13995  */
13996
13997  
13998
13999 /**
14000  * @class Roo.util.ClickRepeater
14001  * @extends Roo.util.Observable
14002  * 
14003  * A wrapper class which can be applied to any element. Fires a "click" event while the
14004  * mouse is pressed. The interval between firings may be specified in the config but
14005  * defaults to 10 milliseconds.
14006  * 
14007  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14008  * 
14009  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14010  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14011  * Similar to an autorepeat key delay.
14012  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14013  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14014  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14015  *           "interval" and "delay" are ignored. "immediate" is honored.
14016  * @cfg {Boolean} preventDefault True to prevent the default click event
14017  * @cfg {Boolean} stopDefault True to stop the default click event
14018  * 
14019  * @history
14020  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14021  *     2007-02-02 jvs Renamed to ClickRepeater
14022  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14023  *
14024  *  @constructor
14025  * @param {String/HTMLElement/Element} el The element to listen on
14026  * @param {Object} config
14027  **/
14028 Roo.util.ClickRepeater = function(el, config)
14029 {
14030     this.el = Roo.get(el);
14031     this.el.unselectable();
14032
14033     Roo.apply(this, config);
14034
14035     this.addEvents({
14036     /**
14037      * @event mousedown
14038      * Fires when the mouse button is depressed.
14039      * @param {Roo.util.ClickRepeater} this
14040      */
14041         "mousedown" : true,
14042     /**
14043      * @event click
14044      * Fires on a specified interval during the time the element is pressed.
14045      * @param {Roo.util.ClickRepeater} this
14046      */
14047         "click" : true,
14048     /**
14049      * @event mouseup
14050      * Fires when the mouse key is released.
14051      * @param {Roo.util.ClickRepeater} this
14052      */
14053         "mouseup" : true
14054     });
14055
14056     this.el.on("mousedown", this.handleMouseDown, this);
14057     if(this.preventDefault || this.stopDefault){
14058         this.el.on("click", function(e){
14059             if(this.preventDefault){
14060                 e.preventDefault();
14061             }
14062             if(this.stopDefault){
14063                 e.stopEvent();
14064             }
14065         }, this);
14066     }
14067
14068     // allow inline handler
14069     if(this.handler){
14070         this.on("click", this.handler,  this.scope || this);
14071     }
14072
14073     Roo.util.ClickRepeater.superclass.constructor.call(this);
14074 };
14075
14076 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14077     interval : 20,
14078     delay: 250,
14079     preventDefault : true,
14080     stopDefault : false,
14081     timer : 0,
14082
14083     // private
14084     handleMouseDown : function(){
14085         clearTimeout(this.timer);
14086         this.el.blur();
14087         if(this.pressClass){
14088             this.el.addClass(this.pressClass);
14089         }
14090         this.mousedownTime = new Date();
14091
14092         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14093         this.el.on("mouseout", this.handleMouseOut, this);
14094
14095         this.fireEvent("mousedown", this);
14096         this.fireEvent("click", this);
14097         
14098         this.timer = this.click.defer(this.delay || this.interval, this);
14099     },
14100
14101     // private
14102     click : function(){
14103         this.fireEvent("click", this);
14104         this.timer = this.click.defer(this.getInterval(), this);
14105     },
14106
14107     // private
14108     getInterval: function(){
14109         if(!this.accelerate){
14110             return this.interval;
14111         }
14112         var pressTime = this.mousedownTime.getElapsed();
14113         if(pressTime < 500){
14114             return 400;
14115         }else if(pressTime < 1700){
14116             return 320;
14117         }else if(pressTime < 2600){
14118             return 250;
14119         }else if(pressTime < 3500){
14120             return 180;
14121         }else if(pressTime < 4400){
14122             return 140;
14123         }else if(pressTime < 5300){
14124             return 80;
14125         }else if(pressTime < 6200){
14126             return 50;
14127         }else{
14128             return 10;
14129         }
14130     },
14131
14132     // private
14133     handleMouseOut : function(){
14134         clearTimeout(this.timer);
14135         if(this.pressClass){
14136             this.el.removeClass(this.pressClass);
14137         }
14138         this.el.on("mouseover", this.handleMouseReturn, this);
14139     },
14140
14141     // private
14142     handleMouseReturn : function(){
14143         this.el.un("mouseover", this.handleMouseReturn);
14144         if(this.pressClass){
14145             this.el.addClass(this.pressClass);
14146         }
14147         this.click();
14148     },
14149
14150     // private
14151     handleMouseUp : function(){
14152         clearTimeout(this.timer);
14153         this.el.un("mouseover", this.handleMouseReturn);
14154         this.el.un("mouseout", this.handleMouseOut);
14155         Roo.get(document).un("mouseup", this.handleMouseUp);
14156         this.el.removeClass(this.pressClass);
14157         this.fireEvent("mouseup", this);
14158     }
14159 });/*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169
14170  
14171 /**
14172  * @class Roo.KeyNav
14173  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14174  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14175  * way to implement custom navigation schemes for any UI component.</p>
14176  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14177  * pageUp, pageDown, del, home, end.  Usage:</p>
14178  <pre><code>
14179 var nav = new Roo.KeyNav("my-element", {
14180     "left" : function(e){
14181         this.moveLeft(e.ctrlKey);
14182     },
14183     "right" : function(e){
14184         this.moveRight(e.ctrlKey);
14185     },
14186     "enter" : function(e){
14187         this.save();
14188     },
14189     scope : this
14190 });
14191 </code></pre>
14192  * @constructor
14193  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14194  * @param {Object} config The config
14195  */
14196 Roo.KeyNav = function(el, config){
14197     this.el = Roo.get(el);
14198     Roo.apply(this, config);
14199     if(!this.disabled){
14200         this.disabled = true;
14201         this.enable();
14202     }
14203 };
14204
14205 Roo.KeyNav.prototype = {
14206     /**
14207      * @cfg {Boolean} disabled
14208      * True to disable this KeyNav instance (defaults to false)
14209      */
14210     disabled : false,
14211     /**
14212      * @cfg {String} defaultEventAction
14213      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14214      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14215      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14216      */
14217     defaultEventAction: "stopEvent",
14218     /**
14219      * @cfg {Boolean} forceKeyDown
14220      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14221      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14222      * handle keydown instead of keypress.
14223      */
14224     forceKeyDown : false,
14225
14226     // private
14227     prepareEvent : function(e){
14228         var k = e.getKey();
14229         var h = this.keyToHandler[k];
14230         //if(h && this[h]){
14231         //    e.stopPropagation();
14232         //}
14233         if(Roo.isSafari && h && k >= 37 && k <= 40){
14234             e.stopEvent();
14235         }
14236     },
14237
14238     // private
14239     relay : function(e){
14240         var k = e.getKey();
14241         var h = this.keyToHandler[k];
14242         if(h && this[h]){
14243             if(this.doRelay(e, this[h], h) !== true){
14244                 e[this.defaultEventAction]();
14245             }
14246         }
14247     },
14248
14249     // private
14250     doRelay : function(e, h, hname){
14251         return h.call(this.scope || this, e);
14252     },
14253
14254     // possible handlers
14255     enter : false,
14256     left : false,
14257     right : false,
14258     up : false,
14259     down : false,
14260     tab : false,
14261     esc : false,
14262     pageUp : false,
14263     pageDown : false,
14264     del : false,
14265     home : false,
14266     end : false,
14267
14268     // quick lookup hash
14269     keyToHandler : {
14270         37 : "left",
14271         39 : "right",
14272         38 : "up",
14273         40 : "down",
14274         33 : "pageUp",
14275         34 : "pageDown",
14276         46 : "del",
14277         36 : "home",
14278         35 : "end",
14279         13 : "enter",
14280         27 : "esc",
14281         9  : "tab"
14282     },
14283
14284         /**
14285          * Enable this KeyNav
14286          */
14287         enable: function(){
14288                 if(this.disabled){
14289             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14290             // the EventObject will normalize Safari automatically
14291             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14292                 this.el.on("keydown", this.relay,  this);
14293             }else{
14294                 this.el.on("keydown", this.prepareEvent,  this);
14295                 this.el.on("keypress", this.relay,  this);
14296             }
14297                     this.disabled = false;
14298                 }
14299         },
14300
14301         /**
14302          * Disable this KeyNav
14303          */
14304         disable: function(){
14305                 if(!this.disabled){
14306                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14307                 this.el.un("keydown", this.relay);
14308             }else{
14309                 this.el.un("keydown", this.prepareEvent);
14310                 this.el.un("keypress", this.relay);
14311             }
14312                     this.disabled = true;
14313                 }
14314         }
14315 };/*
14316  * Based on:
14317  * Ext JS Library 1.1.1
14318  * Copyright(c) 2006-2007, Ext JS, LLC.
14319  *
14320  * Originally Released Under LGPL - original licence link has changed is not relivant.
14321  *
14322  * Fork - LGPL
14323  * <script type="text/javascript">
14324  */
14325
14326  
14327 /**
14328  * @class Roo.KeyMap
14329  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14330  * The constructor accepts the same config object as defined by {@link #addBinding}.
14331  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14332  * combination it will call the function with this signature (if the match is a multi-key
14333  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14334  * A KeyMap can also handle a string representation of keys.<br />
14335  * Usage:
14336  <pre><code>
14337 // map one key by key code
14338 var map = new Roo.KeyMap("my-element", {
14339     key: 13, // or Roo.EventObject.ENTER
14340     fn: myHandler,
14341     scope: myObject
14342 });
14343
14344 // map multiple keys to one action by string
14345 var map = new Roo.KeyMap("my-element", {
14346     key: "a\r\n\t",
14347     fn: myHandler,
14348     scope: myObject
14349 });
14350
14351 // map multiple keys to multiple actions by strings and array of codes
14352 var map = new Roo.KeyMap("my-element", [
14353     {
14354         key: [10,13],
14355         fn: function(){ alert("Return was pressed"); }
14356     }, {
14357         key: "abc",
14358         fn: function(){ alert('a, b or c was pressed'); }
14359     }, {
14360         key: "\t",
14361         ctrl:true,
14362         shift:true,
14363         fn: function(){ alert('Control + shift + tab was pressed.'); }
14364     }
14365 ]);
14366 </code></pre>
14367  * <b>Note: A KeyMap starts enabled</b>
14368  * @constructor
14369  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14370  * @param {Object} config The config (see {@link #addBinding})
14371  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14372  */
14373 Roo.KeyMap = function(el, config, eventName){
14374     this.el  = Roo.get(el);
14375     this.eventName = eventName || "keydown";
14376     this.bindings = [];
14377     if(config){
14378         this.addBinding(config);
14379     }
14380     this.enable();
14381 };
14382
14383 Roo.KeyMap.prototype = {
14384     /**
14385      * True to stop the event from bubbling and prevent the default browser action if the
14386      * key was handled by the KeyMap (defaults to false)
14387      * @type Boolean
14388      */
14389     stopEvent : false,
14390
14391     /**
14392      * Add a new binding to this KeyMap. The following config object properties are supported:
14393      * <pre>
14394 Property    Type             Description
14395 ----------  ---------------  ----------------------------------------------------------------------
14396 key         String/Array     A single keycode or an array of keycodes to handle
14397 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14398 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14399 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14400 fn          Function         The function to call when KeyMap finds the expected key combination
14401 scope       Object           The scope of the callback function
14402 </pre>
14403      *
14404      * Usage:
14405      * <pre><code>
14406 // Create a KeyMap
14407 var map = new Roo.KeyMap(document, {
14408     key: Roo.EventObject.ENTER,
14409     fn: handleKey,
14410     scope: this
14411 });
14412
14413 //Add a new binding to the existing KeyMap later
14414 map.addBinding({
14415     key: 'abc',
14416     shift: true,
14417     fn: handleKey,
14418     scope: this
14419 });
14420 </code></pre>
14421      * @param {Object/Array} config A single KeyMap config or an array of configs
14422      */
14423         addBinding : function(config){
14424         if(config instanceof Array){
14425             for(var i = 0, len = config.length; i < len; i++){
14426                 this.addBinding(config[i]);
14427             }
14428             return;
14429         }
14430         var keyCode = config.key,
14431             shift = config.shift, 
14432             ctrl = config.ctrl, 
14433             alt = config.alt,
14434             fn = config.fn,
14435             scope = config.scope;
14436         if(typeof keyCode == "string"){
14437             var ks = [];
14438             var keyString = keyCode.toUpperCase();
14439             for(var j = 0, len = keyString.length; j < len; j++){
14440                 ks.push(keyString.charCodeAt(j));
14441             }
14442             keyCode = ks;
14443         }
14444         var keyArray = keyCode instanceof Array;
14445         var handler = function(e){
14446             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14447                 var k = e.getKey();
14448                 if(keyArray){
14449                     for(var i = 0, len = keyCode.length; i < len; i++){
14450                         if(keyCode[i] == k){
14451                           if(this.stopEvent){
14452                               e.stopEvent();
14453                           }
14454                           fn.call(scope || window, k, e);
14455                           return;
14456                         }
14457                     }
14458                 }else{
14459                     if(k == keyCode){
14460                         if(this.stopEvent){
14461                            e.stopEvent();
14462                         }
14463                         fn.call(scope || window, k, e);
14464                     }
14465                 }
14466             }
14467         };
14468         this.bindings.push(handler);  
14469         },
14470
14471     /**
14472      * Shorthand for adding a single key listener
14473      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14474      * following options:
14475      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14476      * @param {Function} fn The function to call
14477      * @param {Object} scope (optional) The scope of the function
14478      */
14479     on : function(key, fn, scope){
14480         var keyCode, shift, ctrl, alt;
14481         if(typeof key == "object" && !(key instanceof Array)){
14482             keyCode = key.key;
14483             shift = key.shift;
14484             ctrl = key.ctrl;
14485             alt = key.alt;
14486         }else{
14487             keyCode = key;
14488         }
14489         this.addBinding({
14490             key: keyCode,
14491             shift: shift,
14492             ctrl: ctrl,
14493             alt: alt,
14494             fn: fn,
14495             scope: scope
14496         })
14497     },
14498
14499     // private
14500     handleKeyDown : function(e){
14501             if(this.enabled){ //just in case
14502             var b = this.bindings;
14503             for(var i = 0, len = b.length; i < len; i++){
14504                 b[i].call(this, e);
14505             }
14506             }
14507         },
14508         
14509         /**
14510          * Returns true if this KeyMap is enabled
14511          * @return {Boolean} 
14512          */
14513         isEnabled : function(){
14514             return this.enabled;  
14515         },
14516         
14517         /**
14518          * Enables this KeyMap
14519          */
14520         enable: function(){
14521                 if(!this.enabled){
14522                     this.el.on(this.eventName, this.handleKeyDown, this);
14523                     this.enabled = true;
14524                 }
14525         },
14526
14527         /**
14528          * Disable this KeyMap
14529          */
14530         disable: function(){
14531                 if(this.enabled){
14532                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14533                     this.enabled = false;
14534                 }
14535         }
14536 };/*
14537  * Based on:
14538  * Ext JS Library 1.1.1
14539  * Copyright(c) 2006-2007, Ext JS, LLC.
14540  *
14541  * Originally Released Under LGPL - original licence link has changed is not relivant.
14542  *
14543  * Fork - LGPL
14544  * <script type="text/javascript">
14545  */
14546
14547  
14548 /**
14549  * @class Roo.util.TextMetrics
14550  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14551  * wide, in pixels, a given block of text will be.
14552  * @singleton
14553  */
14554 Roo.util.TextMetrics = function(){
14555     var shared;
14556     return {
14557         /**
14558          * Measures the size of the specified text
14559          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14560          * that can affect the size of the rendered text
14561          * @param {String} text The text to measure
14562          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14563          * in order to accurately measure the text height
14564          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14565          */
14566         measure : function(el, text, fixedWidth){
14567             if(!shared){
14568                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14569             }
14570             shared.bind(el);
14571             shared.setFixedWidth(fixedWidth || 'auto');
14572             return shared.getSize(text);
14573         },
14574
14575         /**
14576          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14577          * the overhead of multiple calls to initialize the style properties on each measurement.
14578          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14579          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14580          * in order to accurately measure the text height
14581          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14582          */
14583         createInstance : function(el, fixedWidth){
14584             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14585         }
14586     };
14587 }();
14588
14589  
14590
14591 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14592     var ml = new Roo.Element(document.createElement('div'));
14593     document.body.appendChild(ml.dom);
14594     ml.position('absolute');
14595     ml.setLeftTop(-1000, -1000);
14596     ml.hide();
14597
14598     if(fixedWidth){
14599         ml.setWidth(fixedWidth);
14600     }
14601      
14602     var instance = {
14603         /**
14604          * Returns the size of the specified text based on the internal element's style and width properties
14605          * @memberOf Roo.util.TextMetrics.Instance#
14606          * @param {String} text The text to measure
14607          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14608          */
14609         getSize : function(text){
14610             ml.update(text);
14611             var s = ml.getSize();
14612             ml.update('');
14613             return s;
14614         },
14615
14616         /**
14617          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14618          * that can affect the size of the rendered text
14619          * @memberOf Roo.util.TextMetrics.Instance#
14620          * @param {String/HTMLElement} el The element, dom node or id
14621          */
14622         bind : function(el){
14623             ml.setStyle(
14624                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14625             );
14626         },
14627
14628         /**
14629          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14630          * to set a fixed width in order to accurately measure the text height.
14631          * @memberOf Roo.util.TextMetrics.Instance#
14632          * @param {Number} width The width to set on the element
14633          */
14634         setFixedWidth : function(width){
14635             ml.setWidth(width);
14636         },
14637
14638         /**
14639          * Returns the measured width of the specified text
14640          * @memberOf Roo.util.TextMetrics.Instance#
14641          * @param {String} text The text to measure
14642          * @return {Number} width The width in pixels
14643          */
14644         getWidth : function(text){
14645             ml.dom.style.width = 'auto';
14646             return this.getSize(text).width;
14647         },
14648
14649         /**
14650          * Returns the measured height of the specified text.  For multiline text, be sure to call
14651          * {@link #setFixedWidth} if necessary.
14652          * @memberOf Roo.util.TextMetrics.Instance#
14653          * @param {String} text The text to measure
14654          * @return {Number} height The height in pixels
14655          */
14656         getHeight : function(text){
14657             return this.getSize(text).height;
14658         }
14659     };
14660
14661     instance.bind(bindTo);
14662
14663     return instance;
14664 };
14665
14666 // backwards compat
14667 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14668  * Based on:
14669  * Ext JS Library 1.1.1
14670  * Copyright(c) 2006-2007, Ext JS, LLC.
14671  *
14672  * Originally Released Under LGPL - original licence link has changed is not relivant.
14673  *
14674  * Fork - LGPL
14675  * <script type="text/javascript">
14676  */
14677
14678 /**
14679  * @class Roo.state.Provider
14680  * Abstract base class for state provider implementations. This class provides methods
14681  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14682  * Provider interface.
14683  */
14684 Roo.state.Provider = function(){
14685     /**
14686      * @event statechange
14687      * Fires when a state change occurs.
14688      * @param {Provider} this This state provider
14689      * @param {String} key The state key which was changed
14690      * @param {String} value The encoded value for the state
14691      */
14692     this.addEvents({
14693         "statechange": true
14694     });
14695     this.state = {};
14696     Roo.state.Provider.superclass.constructor.call(this);
14697 };
14698 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14699     /**
14700      * Returns the current value for a key
14701      * @param {String} name The key name
14702      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14703      * @return {Mixed} The state data
14704      */
14705     get : function(name, defaultValue){
14706         return typeof this.state[name] == "undefined" ?
14707             defaultValue : this.state[name];
14708     },
14709     
14710     /**
14711      * Clears a value from the state
14712      * @param {String} name The key name
14713      */
14714     clear : function(name){
14715         delete this.state[name];
14716         this.fireEvent("statechange", this, name, null);
14717     },
14718     
14719     /**
14720      * Sets the value for a key
14721      * @param {String} name The key name
14722      * @param {Mixed} value The value to set
14723      */
14724     set : function(name, value){
14725         this.state[name] = value;
14726         this.fireEvent("statechange", this, name, value);
14727     },
14728     
14729     /**
14730      * Decodes a string previously encoded with {@link #encodeValue}.
14731      * @param {String} value The value to decode
14732      * @return {Mixed} The decoded value
14733      */
14734     decodeValue : function(cookie){
14735         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14736         var matches = re.exec(unescape(cookie));
14737         if(!matches || !matches[1]) return; // non state cookie
14738         var type = matches[1];
14739         var v = matches[2];
14740         switch(type){
14741             case "n":
14742                 return parseFloat(v);
14743             case "d":
14744                 return new Date(Date.parse(v));
14745             case "b":
14746                 return (v == "1");
14747             case "a":
14748                 var all = [];
14749                 var values = v.split("^");
14750                 for(var i = 0, len = values.length; i < len; i++){
14751                     all.push(this.decodeValue(values[i]));
14752                 }
14753                 return all;
14754            case "o":
14755                 var all = {};
14756                 var values = v.split("^");
14757                 for(var i = 0, len = values.length; i < len; i++){
14758                     var kv = values[i].split("=");
14759                     all[kv[0]] = this.decodeValue(kv[1]);
14760                 }
14761                 return all;
14762            default:
14763                 return v;
14764         }
14765     },
14766     
14767     /**
14768      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14769      * @param {Mixed} value The value to encode
14770      * @return {String} The encoded value
14771      */
14772     encodeValue : function(v){
14773         var enc;
14774         if(typeof v == "number"){
14775             enc = "n:" + v;
14776         }else if(typeof v == "boolean"){
14777             enc = "b:" + (v ? "1" : "0");
14778         }else if(v instanceof Date){
14779             enc = "d:" + v.toGMTString();
14780         }else if(v instanceof Array){
14781             var flat = "";
14782             for(var i = 0, len = v.length; i < len; i++){
14783                 flat += this.encodeValue(v[i]);
14784                 if(i != len-1) flat += "^";
14785             }
14786             enc = "a:" + flat;
14787         }else if(typeof v == "object"){
14788             var flat = "";
14789             for(var key in v){
14790                 if(typeof v[key] != "function"){
14791                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14792                 }
14793             }
14794             enc = "o:" + flat.substring(0, flat.length-1);
14795         }else{
14796             enc = "s:" + v;
14797         }
14798         return escape(enc);        
14799     }
14800 });
14801
14802 /*
14803  * Based on:
14804  * Ext JS Library 1.1.1
14805  * Copyright(c) 2006-2007, Ext JS, LLC.
14806  *
14807  * Originally Released Under LGPL - original licence link has changed is not relivant.
14808  *
14809  * Fork - LGPL
14810  * <script type="text/javascript">
14811  */
14812 /**
14813  * @class Roo.state.Manager
14814  * This is the global state manager. By default all components that are "state aware" check this class
14815  * for state information if you don't pass them a custom state provider. In order for this class
14816  * to be useful, it must be initialized with a provider when your application initializes.
14817  <pre><code>
14818 // in your initialization function
14819 init : function(){
14820    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14821    ...
14822    // supposed you have a {@link Roo.BorderLayout}
14823    var layout = new Roo.BorderLayout(...);
14824    layout.restoreState();
14825    // or a {Roo.BasicDialog}
14826    var dialog = new Roo.BasicDialog(...);
14827    dialog.restoreState();
14828  </code></pre>
14829  * @singleton
14830  */
14831 Roo.state.Manager = function(){
14832     var provider = new Roo.state.Provider();
14833     
14834     return {
14835         /**
14836          * Configures the default state provider for your application
14837          * @param {Provider} stateProvider The state provider to set
14838          */
14839         setProvider : function(stateProvider){
14840             provider = stateProvider;
14841         },
14842         
14843         /**
14844          * Returns the current value for a key
14845          * @param {String} name The key name
14846          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14847          * @return {Mixed} The state data
14848          */
14849         get : function(key, defaultValue){
14850             return provider.get(key, defaultValue);
14851         },
14852         
14853         /**
14854          * Sets the value for a key
14855          * @param {String} name The key name
14856          * @param {Mixed} value The state data
14857          */
14858          set : function(key, value){
14859             provider.set(key, value);
14860         },
14861         
14862         /**
14863          * Clears a value from the state
14864          * @param {String} name The key name
14865          */
14866         clear : function(key){
14867             provider.clear(key);
14868         },
14869         
14870         /**
14871          * Gets the currently configured state provider
14872          * @return {Provider} The state provider
14873          */
14874         getProvider : function(){
14875             return provider;
14876         }
14877     };
14878 }();
14879 /*
14880  * Based on:
14881  * Ext JS Library 1.1.1
14882  * Copyright(c) 2006-2007, Ext JS, LLC.
14883  *
14884  * Originally Released Under LGPL - original licence link has changed is not relivant.
14885  *
14886  * Fork - LGPL
14887  * <script type="text/javascript">
14888  */
14889 /**
14890  * @class Roo.state.CookieProvider
14891  * @extends Roo.state.Provider
14892  * The default Provider implementation which saves state via cookies.
14893  * <br />Usage:
14894  <pre><code>
14895    var cp = new Roo.state.CookieProvider({
14896        path: "/cgi-bin/",
14897        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14898        domain: "roojs.com"
14899    })
14900    Roo.state.Manager.setProvider(cp);
14901  </code></pre>
14902  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14903  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14904  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14905  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14906  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14907  * domain the page is running on including the 'www' like 'www.roojs.com')
14908  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14909  * @constructor
14910  * Create a new CookieProvider
14911  * @param {Object} config The configuration object
14912  */
14913 Roo.state.CookieProvider = function(config){
14914     Roo.state.CookieProvider.superclass.constructor.call(this);
14915     this.path = "/";
14916     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14917     this.domain = null;
14918     this.secure = false;
14919     Roo.apply(this, config);
14920     this.state = this.readCookies();
14921 };
14922
14923 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14924     // private
14925     set : function(name, value){
14926         if(typeof value == "undefined" || value === null){
14927             this.clear(name);
14928             return;
14929         }
14930         this.setCookie(name, value);
14931         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14932     },
14933
14934     // private
14935     clear : function(name){
14936         this.clearCookie(name);
14937         Roo.state.CookieProvider.superclass.clear.call(this, name);
14938     },
14939
14940     // private
14941     readCookies : function(){
14942         var cookies = {};
14943         var c = document.cookie + ";";
14944         var re = /\s?(.*?)=(.*?);/g;
14945         var matches;
14946         while((matches = re.exec(c)) != null){
14947             var name = matches[1];
14948             var value = matches[2];
14949             if(name && name.substring(0,3) == "ys-"){
14950                 cookies[name.substr(3)] = this.decodeValue(value);
14951             }
14952         }
14953         return cookies;
14954     },
14955
14956     // private
14957     setCookie : function(name, value){
14958         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14959            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14960            ((this.path == null) ? "" : ("; path=" + this.path)) +
14961            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14962            ((this.secure == true) ? "; secure" : "");
14963     },
14964
14965     // private
14966     clearCookie : function(name){
14967         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14968            ((this.path == null) ? "" : ("; path=" + this.path)) +
14969            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14970            ((this.secure == true) ? "; secure" : "");
14971     }
14972 });/*
14973  * Based on:
14974  * Ext JS Library 1.1.1
14975  * Copyright(c) 2006-2007, Ext JS, LLC.
14976  *
14977  * Originally Released Under LGPL - original licence link has changed is not relivant.
14978  *
14979  * Fork - LGPL
14980  * <script type="text/javascript">
14981  */
14982  
14983
14984 /**
14985  * @class Roo.ComponentMgr
14986  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14987  * @singleton
14988  */
14989 Roo.ComponentMgr = function(){
14990     var all = new Roo.util.MixedCollection();
14991
14992     return {
14993         /**
14994          * Registers a component.
14995          * @param {Roo.Component} c The component
14996          */
14997         register : function(c){
14998             all.add(c);
14999         },
15000
15001         /**
15002          * Unregisters a component.
15003          * @param {Roo.Component} c The component
15004          */
15005         unregister : function(c){
15006             all.remove(c);
15007         },
15008
15009         /**
15010          * Returns a component by id
15011          * @param {String} id The component id
15012          */
15013         get : function(id){
15014             return all.get(id);
15015         },
15016
15017         /**
15018          * Registers a function that will be called when a specified component is added to ComponentMgr
15019          * @param {String} id The component id
15020          * @param {Funtction} fn The callback function
15021          * @param {Object} scope The scope of the callback
15022          */
15023         onAvailable : function(id, fn, scope){
15024             all.on("add", function(index, o){
15025                 if(o.id == id){
15026                     fn.call(scope || o, o);
15027                     all.un("add", fn, scope);
15028                 }
15029             });
15030         }
15031     };
15032 }();/*
15033  * Based on:
15034  * Ext JS Library 1.1.1
15035  * Copyright(c) 2006-2007, Ext JS, LLC.
15036  *
15037  * Originally Released Under LGPL - original licence link has changed is not relivant.
15038  *
15039  * Fork - LGPL
15040  * <script type="text/javascript">
15041  */
15042  
15043 /**
15044  * @class Roo.Component
15045  * @extends Roo.util.Observable
15046  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15047  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15048  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15049  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15050  * All visual components (widgets) that require rendering into a layout should subclass Component.
15051  * @constructor
15052  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15053  * 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
15054  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15055  */
15056 Roo.Component = function(config){
15057     config = config || {};
15058     if(config.tagName || config.dom || typeof config == "string"){ // element object
15059         config = {el: config, id: config.id || config};
15060     }
15061     this.initialConfig = config;
15062
15063     Roo.apply(this, config);
15064     this.addEvents({
15065         /**
15066          * @event disable
15067          * Fires after the component is disabled.
15068              * @param {Roo.Component} this
15069              */
15070         disable : true,
15071         /**
15072          * @event enable
15073          * Fires after the component is enabled.
15074              * @param {Roo.Component} this
15075              */
15076         enable : true,
15077         /**
15078          * @event beforeshow
15079          * Fires before the component is shown.  Return false to stop the show.
15080              * @param {Roo.Component} this
15081              */
15082         beforeshow : true,
15083         /**
15084          * @event show
15085          * Fires after the component is shown.
15086              * @param {Roo.Component} this
15087              */
15088         show : true,
15089         /**
15090          * @event beforehide
15091          * Fires before the component is hidden. Return false to stop the hide.
15092              * @param {Roo.Component} this
15093              */
15094         beforehide : true,
15095         /**
15096          * @event hide
15097          * Fires after the component is hidden.
15098              * @param {Roo.Component} this
15099              */
15100         hide : true,
15101         /**
15102          * @event beforerender
15103          * Fires before the component is rendered. Return false to stop the render.
15104              * @param {Roo.Component} this
15105              */
15106         beforerender : true,
15107         /**
15108          * @event render
15109          * Fires after the component is rendered.
15110              * @param {Roo.Component} this
15111              */
15112         render : true,
15113         /**
15114          * @event beforedestroy
15115          * Fires before the component is destroyed. Return false to stop the destroy.
15116              * @param {Roo.Component} this
15117              */
15118         beforedestroy : true,
15119         /**
15120          * @event destroy
15121          * Fires after the component is destroyed.
15122              * @param {Roo.Component} this
15123              */
15124         destroy : true
15125     });
15126     if(!this.id){
15127         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15128     }
15129     Roo.ComponentMgr.register(this);
15130     Roo.Component.superclass.constructor.call(this);
15131     this.initComponent();
15132     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15133         this.render(this.renderTo);
15134         delete this.renderTo;
15135     }
15136 };
15137
15138 /** @private */
15139 Roo.Component.AUTO_ID = 1000;
15140
15141 Roo.extend(Roo.Component, Roo.util.Observable, {
15142     /**
15143      * @scope Roo.Component.prototype
15144      * @type {Boolean}
15145      * true if this component is hidden. Read-only.
15146      */
15147     hidden : false,
15148     /**
15149      * @type {Boolean}
15150      * true if this component is disabled. Read-only.
15151      */
15152     disabled : false,
15153     /**
15154      * @type {Boolean}
15155      * true if this component has been rendered. Read-only.
15156      */
15157     rendered : false,
15158     
15159     /** @cfg {String} disableClass
15160      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15161      */
15162     disabledClass : "x-item-disabled",
15163         /** @cfg {Boolean} allowDomMove
15164          * Whether the component can move the Dom node when rendering (defaults to true).
15165          */
15166     allowDomMove : true,
15167     /** @cfg {String} hideMode
15168      * How this component should hidden. Supported values are
15169      * "visibility" (css visibility), "offsets" (negative offset position) and
15170      * "display" (css display) - defaults to "display".
15171      */
15172     hideMode: 'display',
15173
15174     /** @private */
15175     ctype : "Roo.Component",
15176
15177     /**
15178      * @cfg {String} actionMode 
15179      * which property holds the element that used for  hide() / show() / disable() / enable()
15180      * default is 'el' 
15181      */
15182     actionMode : "el",
15183
15184     /** @private */
15185     getActionEl : function(){
15186         return this[this.actionMode];
15187     },
15188
15189     initComponent : Roo.emptyFn,
15190     /**
15191      * If this is a lazy rendering component, render it to its container element.
15192      * @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.
15193      */
15194     render : function(container, position){
15195         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15196             if(!container && this.el){
15197                 this.el = Roo.get(this.el);
15198                 container = this.el.dom.parentNode;
15199                 this.allowDomMove = false;
15200             }
15201             this.container = Roo.get(container);
15202             this.rendered = true;
15203             if(position !== undefined){
15204                 if(typeof position == 'number'){
15205                     position = this.container.dom.childNodes[position];
15206                 }else{
15207                     position = Roo.getDom(position);
15208                 }
15209             }
15210             this.onRender(this.container, position || null);
15211             if(this.cls){
15212                 this.el.addClass(this.cls);
15213                 delete this.cls;
15214             }
15215             if(this.style){
15216                 this.el.applyStyles(this.style);
15217                 delete this.style;
15218             }
15219             this.fireEvent("render", this);
15220             this.afterRender(this.container);
15221             if(this.hidden){
15222                 this.hide();
15223             }
15224             if(this.disabled){
15225                 this.disable();
15226             }
15227         }
15228         return this;
15229     },
15230
15231     /** @private */
15232     // default function is not really useful
15233     onRender : function(ct, position){
15234         if(this.el){
15235             this.el = Roo.get(this.el);
15236             if(this.allowDomMove !== false){
15237                 ct.dom.insertBefore(this.el.dom, position);
15238             }
15239         }
15240     },
15241
15242     /** @private */
15243     getAutoCreate : function(){
15244         var cfg = typeof this.autoCreate == "object" ?
15245                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15246         if(this.id && !cfg.id){
15247             cfg.id = this.id;
15248         }
15249         return cfg;
15250     },
15251
15252     /** @private */
15253     afterRender : Roo.emptyFn,
15254
15255     /**
15256      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15257      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15258      */
15259     destroy : function(){
15260         if(this.fireEvent("beforedestroy", this) !== false){
15261             this.purgeListeners();
15262             this.beforeDestroy();
15263             if(this.rendered){
15264                 this.el.removeAllListeners();
15265                 this.el.remove();
15266                 if(this.actionMode == "container"){
15267                     this.container.remove();
15268                 }
15269             }
15270             this.onDestroy();
15271             Roo.ComponentMgr.unregister(this);
15272             this.fireEvent("destroy", this);
15273         }
15274     },
15275
15276         /** @private */
15277     beforeDestroy : function(){
15278
15279     },
15280
15281         /** @private */
15282         onDestroy : function(){
15283
15284     },
15285
15286     /**
15287      * Returns the underlying {@link Roo.Element}.
15288      * @return {Roo.Element} The element
15289      */
15290     getEl : function(){
15291         return this.el;
15292     },
15293
15294     /**
15295      * Returns the id of this component.
15296      * @return {String}
15297      */
15298     getId : function(){
15299         return this.id;
15300     },
15301
15302     /**
15303      * Try to focus this component.
15304      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15305      * @return {Roo.Component} this
15306      */
15307     focus : function(selectText){
15308         if(this.rendered){
15309             this.el.focus();
15310             if(selectText === true){
15311                 this.el.dom.select();
15312             }
15313         }
15314         return this;
15315     },
15316
15317     /** @private */
15318     blur : function(){
15319         if(this.rendered){
15320             this.el.blur();
15321         }
15322         return this;
15323     },
15324
15325     /**
15326      * Disable this component.
15327      * @return {Roo.Component} this
15328      */
15329     disable : function(){
15330         if(this.rendered){
15331             this.onDisable();
15332         }
15333         this.disabled = true;
15334         this.fireEvent("disable", this);
15335         return this;
15336     },
15337
15338         // private
15339     onDisable : function(){
15340         this.getActionEl().addClass(this.disabledClass);
15341         this.el.dom.disabled = true;
15342     },
15343
15344     /**
15345      * Enable this component.
15346      * @return {Roo.Component} this
15347      */
15348     enable : function(){
15349         if(this.rendered){
15350             this.onEnable();
15351         }
15352         this.disabled = false;
15353         this.fireEvent("enable", this);
15354         return this;
15355     },
15356
15357         // private
15358     onEnable : function(){
15359         this.getActionEl().removeClass(this.disabledClass);
15360         this.el.dom.disabled = false;
15361     },
15362
15363     /**
15364      * Convenience function for setting disabled/enabled by boolean.
15365      * @param {Boolean} disabled
15366      */
15367     setDisabled : function(disabled){
15368         this[disabled ? "disable" : "enable"]();
15369     },
15370
15371     /**
15372      * Show this component.
15373      * @return {Roo.Component} this
15374      */
15375     show: function(){
15376         if(this.fireEvent("beforeshow", this) !== false){
15377             this.hidden = false;
15378             if(this.rendered){
15379                 this.onShow();
15380             }
15381             this.fireEvent("show", this);
15382         }
15383         return this;
15384     },
15385
15386     // private
15387     onShow : function(){
15388         var ae = this.getActionEl();
15389         if(this.hideMode == 'visibility'){
15390             ae.dom.style.visibility = "visible";
15391         }else if(this.hideMode == 'offsets'){
15392             ae.removeClass('x-hidden');
15393         }else{
15394             ae.dom.style.display = "";
15395         }
15396     },
15397
15398     /**
15399      * Hide this component.
15400      * @return {Roo.Component} this
15401      */
15402     hide: function(){
15403         if(this.fireEvent("beforehide", this) !== false){
15404             this.hidden = true;
15405             if(this.rendered){
15406                 this.onHide();
15407             }
15408             this.fireEvent("hide", this);
15409         }
15410         return this;
15411     },
15412
15413     // private
15414     onHide : function(){
15415         var ae = this.getActionEl();
15416         if(this.hideMode == 'visibility'){
15417             ae.dom.style.visibility = "hidden";
15418         }else if(this.hideMode == 'offsets'){
15419             ae.addClass('x-hidden');
15420         }else{
15421             ae.dom.style.display = "none";
15422         }
15423     },
15424
15425     /**
15426      * Convenience function to hide or show this component by boolean.
15427      * @param {Boolean} visible True to show, false to hide
15428      * @return {Roo.Component} this
15429      */
15430     setVisible: function(visible){
15431         if(visible) {
15432             this.show();
15433         }else{
15434             this.hide();
15435         }
15436         return this;
15437     },
15438
15439     /**
15440      * Returns true if this component is visible.
15441      */
15442     isVisible : function(){
15443         return this.getActionEl().isVisible();
15444     },
15445
15446     cloneConfig : function(overrides){
15447         overrides = overrides || {};
15448         var id = overrides.id || Roo.id();
15449         var cfg = Roo.applyIf(overrides, this.initialConfig);
15450         cfg.id = id; // prevent dup id
15451         return new this.constructor(cfg);
15452     }
15453 });/*
15454  * Based on:
15455  * Ext JS Library 1.1.1
15456  * Copyright(c) 2006-2007, Ext JS, LLC.
15457  *
15458  * Originally Released Under LGPL - original licence link has changed is not relivant.
15459  *
15460  * Fork - LGPL
15461  * <script type="text/javascript">
15462  */
15463
15464 /**
15465  * @class Roo.BoxComponent
15466  * @extends Roo.Component
15467  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15468  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15469  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15470  * layout containers.
15471  * @constructor
15472  * @param {Roo.Element/String/Object} config The configuration options.
15473  */
15474 Roo.BoxComponent = function(config){
15475     Roo.Component.call(this, config);
15476     this.addEvents({
15477         /**
15478          * @event resize
15479          * Fires after the component is resized.
15480              * @param {Roo.Component} this
15481              * @param {Number} adjWidth The box-adjusted width that was set
15482              * @param {Number} adjHeight The box-adjusted height that was set
15483              * @param {Number} rawWidth The width that was originally specified
15484              * @param {Number} rawHeight The height that was originally specified
15485              */
15486         resize : true,
15487         /**
15488          * @event move
15489          * Fires after the component is moved.
15490              * @param {Roo.Component} this
15491              * @param {Number} x The new x position
15492              * @param {Number} y The new y position
15493              */
15494         move : true
15495     });
15496 };
15497
15498 Roo.extend(Roo.BoxComponent, Roo.Component, {
15499     // private, set in afterRender to signify that the component has been rendered
15500     boxReady : false,
15501     // private, used to defer height settings to subclasses
15502     deferHeight: false,
15503     /** @cfg {Number} width
15504      * width (optional) size of component
15505      */
15506      /** @cfg {Number} height
15507      * height (optional) size of component
15508      */
15509      
15510     /**
15511      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15512      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15513      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15514      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15515      * @return {Roo.BoxComponent} this
15516      */
15517     setSize : function(w, h){
15518         // support for standard size objects
15519         if(typeof w == 'object'){
15520             h = w.height;
15521             w = w.width;
15522         }
15523         // not rendered
15524         if(!this.boxReady){
15525             this.width = w;
15526             this.height = h;
15527             return this;
15528         }
15529
15530         // prevent recalcs when not needed
15531         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15532             return this;
15533         }
15534         this.lastSize = {width: w, height: h};
15535
15536         var adj = this.adjustSize(w, h);
15537         var aw = adj.width, ah = adj.height;
15538         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15539             var rz = this.getResizeEl();
15540             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15541                 rz.setSize(aw, ah);
15542             }else if(!this.deferHeight && ah !== undefined){
15543                 rz.setHeight(ah);
15544             }else if(aw !== undefined){
15545                 rz.setWidth(aw);
15546             }
15547             this.onResize(aw, ah, w, h);
15548             this.fireEvent('resize', this, aw, ah, w, h);
15549         }
15550         return this;
15551     },
15552
15553     /**
15554      * Gets the current size of the component's underlying element.
15555      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15556      */
15557     getSize : function(){
15558         return this.el.getSize();
15559     },
15560
15561     /**
15562      * Gets the current XY position of the component's underlying element.
15563      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15564      * @return {Array} The XY position of the element (e.g., [100, 200])
15565      */
15566     getPosition : function(local){
15567         if(local === true){
15568             return [this.el.getLeft(true), this.el.getTop(true)];
15569         }
15570         return this.xy || this.el.getXY();
15571     },
15572
15573     /**
15574      * Gets the current box measurements of the component's underlying element.
15575      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15576      * @returns {Object} box An object in the format {x, y, width, height}
15577      */
15578     getBox : function(local){
15579         var s = this.el.getSize();
15580         if(local){
15581             s.x = this.el.getLeft(true);
15582             s.y = this.el.getTop(true);
15583         }else{
15584             var xy = this.xy || this.el.getXY();
15585             s.x = xy[0];
15586             s.y = xy[1];
15587         }
15588         return s;
15589     },
15590
15591     /**
15592      * Sets the current box measurements of the component's underlying element.
15593      * @param {Object} box An object in the format {x, y, width, height}
15594      * @returns {Roo.BoxComponent} this
15595      */
15596     updateBox : function(box){
15597         this.setSize(box.width, box.height);
15598         this.setPagePosition(box.x, box.y);
15599         return this;
15600     },
15601
15602     // protected
15603     getResizeEl : function(){
15604         return this.resizeEl || this.el;
15605     },
15606
15607     // protected
15608     getPositionEl : function(){
15609         return this.positionEl || this.el;
15610     },
15611
15612     /**
15613      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15614      * This method fires the move event.
15615      * @param {Number} left The new left
15616      * @param {Number} top The new top
15617      * @returns {Roo.BoxComponent} this
15618      */
15619     setPosition : function(x, y){
15620         this.x = x;
15621         this.y = y;
15622         if(!this.boxReady){
15623             return this;
15624         }
15625         var adj = this.adjustPosition(x, y);
15626         var ax = adj.x, ay = adj.y;
15627
15628         var el = this.getPositionEl();
15629         if(ax !== undefined || ay !== undefined){
15630             if(ax !== undefined && ay !== undefined){
15631                 el.setLeftTop(ax, ay);
15632             }else if(ax !== undefined){
15633                 el.setLeft(ax);
15634             }else if(ay !== undefined){
15635                 el.setTop(ay);
15636             }
15637             this.onPosition(ax, ay);
15638             this.fireEvent('move', this, ax, ay);
15639         }
15640         return this;
15641     },
15642
15643     /**
15644      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15645      * This method fires the move event.
15646      * @param {Number} x The new x position
15647      * @param {Number} y The new y position
15648      * @returns {Roo.BoxComponent} this
15649      */
15650     setPagePosition : function(x, y){
15651         this.pageX = x;
15652         this.pageY = y;
15653         if(!this.boxReady){
15654             return;
15655         }
15656         if(x === undefined || y === undefined){ // cannot translate undefined points
15657             return;
15658         }
15659         var p = this.el.translatePoints(x, y);
15660         this.setPosition(p.left, p.top);
15661         return this;
15662     },
15663
15664     // private
15665     onRender : function(ct, position){
15666         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15667         if(this.resizeEl){
15668             this.resizeEl = Roo.get(this.resizeEl);
15669         }
15670         if(this.positionEl){
15671             this.positionEl = Roo.get(this.positionEl);
15672         }
15673     },
15674
15675     // private
15676     afterRender : function(){
15677         Roo.BoxComponent.superclass.afterRender.call(this);
15678         this.boxReady = true;
15679         this.setSize(this.width, this.height);
15680         if(this.x || this.y){
15681             this.setPosition(this.x, this.y);
15682         }
15683         if(this.pageX || this.pageY){
15684             this.setPagePosition(this.pageX, this.pageY);
15685         }
15686     },
15687
15688     /**
15689      * Force the component's size to recalculate based on the underlying element's current height and width.
15690      * @returns {Roo.BoxComponent} this
15691      */
15692     syncSize : function(){
15693         delete this.lastSize;
15694         this.setSize(this.el.getWidth(), this.el.getHeight());
15695         return this;
15696     },
15697
15698     /**
15699      * Called after the component is resized, this method is empty by default but can be implemented by any
15700      * subclass that needs to perform custom logic after a resize occurs.
15701      * @param {Number} adjWidth The box-adjusted width that was set
15702      * @param {Number} adjHeight The box-adjusted height that was set
15703      * @param {Number} rawWidth The width that was originally specified
15704      * @param {Number} rawHeight The height that was originally specified
15705      */
15706     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15707
15708     },
15709
15710     /**
15711      * Called after the component is moved, this method is empty by default but can be implemented by any
15712      * subclass that needs to perform custom logic after a move occurs.
15713      * @param {Number} x The new x position
15714      * @param {Number} y The new y position
15715      */
15716     onPosition : function(x, y){
15717
15718     },
15719
15720     // private
15721     adjustSize : function(w, h){
15722         if(this.autoWidth){
15723             w = 'auto';
15724         }
15725         if(this.autoHeight){
15726             h = 'auto';
15727         }
15728         return {width : w, height: h};
15729     },
15730
15731     // private
15732     adjustPosition : function(x, y){
15733         return {x : x, y: y};
15734     }
15735 });/*
15736  * Original code for Roojs - LGPL
15737  * <script type="text/javascript">
15738  */
15739  
15740 /**
15741  * @class Roo.XComponent
15742  * A delayed Element creator...
15743  * Or a way to group chunks of interface together.
15744  * 
15745  * Mypart.xyx = new Roo.XComponent({
15746
15747     parent : 'Mypart.xyz', // empty == document.element.!!
15748     order : '001',
15749     name : 'xxxx'
15750     region : 'xxxx'
15751     disabled : function() {} 
15752      
15753     tree : function() { // return an tree of xtype declared components
15754         var MODULE = this;
15755         return 
15756         {
15757             xtype : 'NestedLayoutPanel',
15758             // technicall
15759         }
15760      ]
15761  *})
15762  *
15763  *
15764  * It can be used to build a big heiracy, with parent etc.
15765  * or you can just use this to render a single compoent to a dom element
15766  * MYPART.render(Roo.Element | String(id) | dom_element )
15767  * 
15768  * @extends Roo.util.Observable
15769  * @constructor
15770  * @param cfg {Object} configuration of component
15771  * 
15772  */
15773 Roo.XComponent = function(cfg) {
15774     Roo.apply(this, cfg);
15775     this.addEvents({ 
15776         /**
15777              * @event built
15778              * Fires when this the componnt is built
15779              * @param {Roo.XComponent} c the component
15780              */
15781         'built' : true
15782         
15783     });
15784     this.region = this.region || 'center'; // default..
15785     Roo.XComponent.register(this);
15786     this.modules = false;
15787     this.el = false; // where the layout goes..
15788     
15789     
15790 }
15791 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15792     /**
15793      * @property el
15794      * The created element (with Roo.factory())
15795      * @type {Roo.Layout}
15796      */
15797     el  : false,
15798     
15799     /**
15800      * @property el
15801      * for BC  - use el in new code
15802      * @type {Roo.Layout}
15803      */
15804     panel : false,
15805     
15806     /**
15807      * @property layout
15808      * for BC  - use el in new code
15809      * @type {Roo.Layout}
15810      */
15811     layout : false,
15812     
15813      /**
15814      * @cfg {Function|boolean} disabled
15815      * If this module is disabled by some rule, return true from the funtion
15816      */
15817     disabled : false,
15818     
15819     /**
15820      * @cfg {String} parent 
15821      * Name of parent element which it get xtype added to..
15822      */
15823     parent: false,
15824     
15825     /**
15826      * @cfg {String} order
15827      * Used to set the order in which elements are created (usefull for multiple tabs)
15828      */
15829     
15830     order : false,
15831     /**
15832      * @cfg {String} name
15833      * String to display while loading.
15834      */
15835     name : false,
15836     /**
15837      * @cfg {String} region
15838      * Region to render component to (defaults to center)
15839      */
15840     region : 'center',
15841     
15842     /**
15843      * @cfg {Array} items
15844      * A single item array - the first element is the root of the tree..
15845      * It's done this way to stay compatible with the Xtype system...
15846      */
15847     items : false,
15848     
15849     /**
15850      * @property _tree
15851      * The method that retuns the tree of parts that make up this compoennt 
15852      * @type {function}
15853      */
15854     _tree  : false,
15855     
15856      /**
15857      * render
15858      * render element to dom or tree
15859      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15860      */
15861     
15862     render : function(el)
15863     {
15864         
15865         el = el || false;
15866         var hp = this.parent ? 1 : 0;
15867         
15868         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15869             // if parent is a '#.....' string, then let's use that..
15870             var ename = this.parent.substr(1)
15871             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15872             el = Roo.get(ename);
15873             if (!el && !this.parent) {
15874                 Roo.log("Warning - element can not be found :#" + ename );
15875                 return;
15876             }
15877         }
15878         var tree = this._tree ? this._tree() : this.tree();
15879
15880         // altertive root elements ??? - we need a better way to indicate these.
15881         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15882                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15883         
15884         if (!this.parent && is_alt) {
15885             //el = Roo.get(document.body);
15886             this.parent = { el : true };
15887         }
15888             
15889             
15890         
15891         if (!this.parent) {
15892             
15893             Roo.log("no parent - creating one");
15894             
15895             el = el ? Roo.get(el) : false;      
15896             
15897             // it's a top level one..
15898             this.parent =  {
15899                 el : new Roo.BorderLayout(el || document.body, {
15900                 
15901                      center: {
15902                          titlebar: false,
15903                          autoScroll:false,
15904                          closeOnTab: true,
15905                          tabPosition: 'top',
15906                           //resizeTabs: true,
15907                          alwaysShowTabs: el && hp? false :  true,
15908                          hideTabs: el || !hp ? true :  false,
15909                          minTabWidth: 140
15910                      }
15911                  })
15912             }
15913         }
15914         
15915                 if (!this.parent.el) {
15916                         // probably an old style ctor, which has been disabled.
15917                         return;
15918                         
15919                 }
15920                 // The 'tree' method is  '_tree now' 
15921             
15922         tree.region = tree.region || this.region;
15923         
15924         if (this.parent.el === true) {
15925             // bootstrap... - body..
15926             this.parent.el = Roo.factory(tree);
15927         }
15928         
15929         this.el = this.parent.el.addxtype(tree);
15930         this.fireEvent('built', this);
15931         
15932         this.panel = this.el;
15933         this.layout = this.panel.layout;
15934                 this.parentLayout = this.parent.layout  || false;  
15935          
15936     }
15937     
15938 });
15939
15940 Roo.apply(Roo.XComponent, {
15941     /**
15942      * @property  hideProgress
15943      * true to disable the building progress bar.. usefull on single page renders.
15944      * @type Boolean
15945      */
15946     hideProgress : false,
15947     /**
15948      * @property  buildCompleted
15949      * True when the builder has completed building the interface.
15950      * @type Boolean
15951      */
15952     buildCompleted : false,
15953      
15954     /**
15955      * @property  topModule
15956      * the upper most module - uses document.element as it's constructor.
15957      * @type Object
15958      */
15959      
15960     topModule  : false,
15961       
15962     /**
15963      * @property  modules
15964      * array of modules to be created by registration system.
15965      * @type {Array} of Roo.XComponent
15966      */
15967     
15968     modules : [],
15969     /**
15970      * @property  elmodules
15971      * array of modules to be created by which use #ID 
15972      * @type {Array} of Roo.XComponent
15973      */
15974      
15975     elmodules : [],
15976
15977      /**
15978      * @property  build_from_html
15979      * Build elements from html - used by bootstrap HTML stuff 
15980      *    - this is cleared after build is completed
15981      * @type {boolean} true  (default false)
15982      */
15983      
15984     build_from_html : false,
15985
15986     /**
15987      * Register components to be built later.
15988      *
15989      * This solves the following issues
15990      * - Building is not done on page load, but after an authentication process has occured.
15991      * - Interface elements are registered on page load
15992      * - Parent Interface elements may not be loaded before child, so this handles that..
15993      * 
15994      *
15995      * example:
15996      * 
15997      * MyApp.register({
15998           order : '000001',
15999           module : 'Pman.Tab.projectMgr',
16000           region : 'center',
16001           parent : 'Pman.layout',
16002           disabled : false,  // or use a function..
16003         })
16004      
16005      * * @param {Object} details about module
16006      */
16007     register : function(obj) {
16008                 
16009         Roo.XComponent.event.fireEvent('register', obj);
16010         switch(typeof(obj.disabled) ) {
16011                 
16012             case 'undefined':
16013                 break;
16014             
16015             case 'function':
16016                 if ( obj.disabled() ) {
16017                         return;
16018                 }
16019                 break;
16020             
16021             default:
16022                 if (obj.disabled) {
16023                         return;
16024                 }
16025                 break;
16026         }
16027                 
16028         this.modules.push(obj);
16029          
16030     },
16031     /**
16032      * convert a string to an object..
16033      * eg. 'AAA.BBB' -> finds AAA.BBB
16034
16035      */
16036     
16037     toObject : function(str)
16038     {
16039         if (!str || typeof(str) == 'object') {
16040             return str;
16041         }
16042         if (str.substring(0,1) == '#') {
16043             return str;
16044         }
16045
16046         var ar = str.split('.');
16047         var rt, o;
16048         rt = ar.shift();
16049             /** eval:var:o */
16050         try {
16051             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16052         } catch (e) {
16053             throw "Module not found : " + str;
16054         }
16055         
16056         if (o === false) {
16057             throw "Module not found : " + str;
16058         }
16059         Roo.each(ar, function(e) {
16060             if (typeof(o[e]) == 'undefined') {
16061                 throw "Module not found : " + str;
16062             }
16063             o = o[e];
16064         });
16065         
16066         return o;
16067         
16068     },
16069     
16070     
16071     /**
16072      * move modules into their correct place in the tree..
16073      * 
16074      */
16075     preBuild : function ()
16076     {
16077         var _t = this;
16078         Roo.each(this.modules , function (obj)
16079         {
16080             Roo.XComponent.event.fireEvent('beforebuild', obj);
16081             
16082             var opar = obj.parent;
16083             try { 
16084                 obj.parent = this.toObject(opar);
16085             } catch(e) {
16086                 Roo.log("parent:toObject failed: " + e.toString());
16087                 return;
16088             }
16089             
16090             if (!obj.parent) {
16091                 Roo.debug && Roo.log("GOT top level module");
16092                 Roo.debug && Roo.log(obj);
16093                 obj.modules = new Roo.util.MixedCollection(false, 
16094                     function(o) { return o.order + '' }
16095                 );
16096                 this.topModule = obj;
16097                 return;
16098             }
16099                         // parent is a string (usually a dom element name..)
16100             if (typeof(obj.parent) == 'string') {
16101                 this.elmodules.push(obj);
16102                 return;
16103             }
16104             if (obj.parent.constructor != Roo.XComponent) {
16105                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16106             }
16107             if (!obj.parent.modules) {
16108                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16109                     function(o) { return o.order + '' }
16110                 );
16111             }
16112             if (obj.parent.disabled) {
16113                 obj.disabled = true;
16114             }
16115             obj.parent.modules.add(obj);
16116         }, this);
16117     },
16118     
16119      /**
16120      * make a list of modules to build.
16121      * @return {Array} list of modules. 
16122      */ 
16123     
16124     buildOrder : function()
16125     {
16126         var _this = this;
16127         var cmp = function(a,b) {   
16128             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16129         };
16130         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16131             throw "No top level modules to build";
16132         }
16133         
16134         // make a flat list in order of modules to build.
16135         var mods = this.topModule ? [ this.topModule ] : [];
16136                 
16137         
16138         // elmodules (is a list of DOM based modules )
16139         Roo.each(this.elmodules, function(e) {
16140             mods.push(e);
16141             if (!this.topModule &&
16142                 typeof(e.parent) == 'string' &&
16143                 e.parent.substring(0,1) == '#' &&
16144                 Roo.get(e.parent.substr(1))
16145                ) {
16146                 
16147                 _this.topModule = e;
16148             }
16149             
16150         });
16151
16152         
16153         // add modules to their parents..
16154         var addMod = function(m) {
16155             Roo.debug && Roo.log("build Order: add: " + m.name);
16156                 
16157             mods.push(m);
16158             if (m.modules && !m.disabled) {
16159                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16160                 m.modules.keySort('ASC',  cmp );
16161                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16162     
16163                 m.modules.each(addMod);
16164             } else {
16165                 Roo.debug && Roo.log("build Order: no child modules");
16166             }
16167             // not sure if this is used any more..
16168             if (m.finalize) {
16169                 m.finalize.name = m.name + " (clean up) ";
16170                 mods.push(m.finalize);
16171             }
16172             
16173         }
16174         if (this.topModule && this.topModule.modules) { 
16175             this.topModule.modules.keySort('ASC',  cmp );
16176             this.topModule.modules.each(addMod);
16177         } 
16178         return mods;
16179     },
16180     
16181      /**
16182      * Build the registered modules.
16183      * @param {Object} parent element.
16184      * @param {Function} optional method to call after module has been added.
16185      * 
16186      */ 
16187    
16188     build : function(opts) 
16189     {
16190         
16191         if (typeof(opts) != 'undefined') {
16192             Roo.apply(this,opts);
16193         }
16194         
16195         this.preBuild();
16196         var mods = this.buildOrder();
16197       
16198         //this.allmods = mods;
16199         //Roo.debug && Roo.log(mods);
16200         //return;
16201         if (!mods.length) { // should not happen
16202             throw "NO modules!!!";
16203         }
16204         
16205         
16206         var msg = "Building Interface...";
16207         // flash it up as modal - so we store the mask!?
16208         if (!this.hideProgress && Roo.MessageBox) {
16209             Roo.MessageBox.show({ title: 'loading' });
16210             Roo.MessageBox.show({
16211                title: "Please wait...",
16212                msg: msg,
16213                width:450,
16214                progress:true,
16215                closable:false,
16216                modal: false
16217               
16218             });
16219         }
16220         var total = mods.length;
16221         
16222         var _this = this;
16223         var progressRun = function() {
16224             if (!mods.length) {
16225                 Roo.debug && Roo.log('hide?');
16226                 if (!this.hideProgress && Roo.MessageBox) {
16227                     Roo.MessageBox.hide();
16228                 }
16229                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16230                 
16231                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16232                 
16233                 // THE END...
16234                 return false;   
16235             }
16236             
16237             var m = mods.shift();
16238             
16239             
16240             Roo.debug && Roo.log(m);
16241             // not sure if this is supported any more.. - modules that are are just function
16242             if (typeof(m) == 'function') { 
16243                 m.call(this);
16244                 return progressRun.defer(10, _this);
16245             } 
16246             
16247             
16248             msg = "Building Interface " + (total  - mods.length) + 
16249                     " of " + total + 
16250                     (m.name ? (' - ' + m.name) : '');
16251                         Roo.debug && Roo.log(msg);
16252             if (!this.hideProgress &&  Roo.MessageBox) { 
16253                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16254             }
16255             
16256          
16257             // is the module disabled?
16258             var disabled = (typeof(m.disabled) == 'function') ?
16259                 m.disabled.call(m.module.disabled) : m.disabled;    
16260             
16261             
16262             if (disabled) {
16263                 return progressRun(); // we do not update the display!
16264             }
16265             
16266             // now build 
16267             
16268                         
16269                         
16270             m.render();
16271             // it's 10 on top level, and 1 on others??? why...
16272             return progressRun.defer(10, _this);
16273              
16274         }
16275         progressRun.defer(1, _this);
16276      
16277         
16278         
16279     },
16280         
16281         
16282         /**
16283          * Event Object.
16284          *
16285          *
16286          */
16287         event: false, 
16288     /**
16289          * wrapper for event.on - aliased later..  
16290          * Typically use to register a event handler for register:
16291          *
16292          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16293          *
16294          */
16295     on : false
16296    
16297     
16298     
16299 });
16300
16301 Roo.XComponent.event = new Roo.util.Observable({
16302                 events : { 
16303                         /**
16304                          * @event register
16305                          * Fires when an Component is registered,
16306                          * set the disable property on the Component to stop registration.
16307                          * @param {Roo.XComponent} c the component being registerd.
16308                          * 
16309                          */
16310                         'register' : true,
16311             /**
16312                          * @event beforebuild
16313                          * Fires before each Component is built
16314                          * can be used to apply permissions.
16315                          * @param {Roo.XComponent} c the component being registerd.
16316                          * 
16317                          */
16318                         'beforebuild' : true,
16319                         /**
16320                          * @event buildcomplete
16321                          * Fires on the top level element when all elements have been built
16322                          * @param {Roo.XComponent} the top level component.
16323                          */
16324                         'buildcomplete' : true
16325                         
16326                 }
16327 });
16328
16329 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16330  /*
16331  * Based on:
16332  * Ext JS Library 1.1.1
16333  * Copyright(c) 2006-2007, Ext JS, LLC.
16334  *
16335  * Originally Released Under LGPL - original licence link has changed is not relivant.
16336  *
16337  * Fork - LGPL
16338  * <script type="text/javascript">
16339  */
16340
16341
16342
16343 /*
16344  * These classes are derivatives of the similarly named classes in the YUI Library.
16345  * The original license:
16346  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16347  * Code licensed under the BSD License:
16348  * http://developer.yahoo.net/yui/license.txt
16349  */
16350
16351 (function() {
16352
16353 var Event=Roo.EventManager;
16354 var Dom=Roo.lib.Dom;
16355
16356 /**
16357  * @class Roo.dd.DragDrop
16358  * @extends Roo.util.Observable
16359  * Defines the interface and base operation of items that that can be
16360  * dragged or can be drop targets.  It was designed to be extended, overriding
16361  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16362  * Up to three html elements can be associated with a DragDrop instance:
16363  * <ul>
16364  * <li>linked element: the element that is passed into the constructor.
16365  * This is the element which defines the boundaries for interaction with
16366  * other DragDrop objects.</li>
16367  * <li>handle element(s): The drag operation only occurs if the element that
16368  * was clicked matches a handle element.  By default this is the linked
16369  * element, but there are times that you will want only a portion of the
16370  * linked element to initiate the drag operation, and the setHandleElId()
16371  * method provides a way to define this.</li>
16372  * <li>drag element: this represents the element that would be moved along
16373  * with the cursor during a drag operation.  By default, this is the linked
16374  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16375  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16376  * </li>
16377  * </ul>
16378  * This class should not be instantiated until the onload event to ensure that
16379  * the associated elements are available.
16380  * The following would define a DragDrop obj that would interact with any
16381  * other DragDrop obj in the "group1" group:
16382  * <pre>
16383  *  dd = new Roo.dd.DragDrop("div1", "group1");
16384  * </pre>
16385  * Since none of the event handlers have been implemented, nothing would
16386  * actually happen if you were to run the code above.  Normally you would
16387  * override this class or one of the default implementations, but you can
16388  * also override the methods you want on an instance of the class...
16389  * <pre>
16390  *  dd.onDragDrop = function(e, id) {
16391  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16392  *  }
16393  * </pre>
16394  * @constructor
16395  * @param {String} id of the element that is linked to this instance
16396  * @param {String} sGroup the group of related DragDrop objects
16397  * @param {object} config an object containing configurable attributes
16398  *                Valid properties for DragDrop:
16399  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16400  */
16401 Roo.dd.DragDrop = function(id, sGroup, config) {
16402     if (id) {
16403         this.init(id, sGroup, config);
16404     }
16405     
16406 };
16407
16408 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16409
16410     /**
16411      * The id of the element associated with this object.  This is what we
16412      * refer to as the "linked element" because the size and position of
16413      * this element is used to determine when the drag and drop objects have
16414      * interacted.
16415      * @property id
16416      * @type String
16417      */
16418     id: null,
16419
16420     /**
16421      * Configuration attributes passed into the constructor
16422      * @property config
16423      * @type object
16424      */
16425     config: null,
16426
16427     /**
16428      * The id of the element that will be dragged.  By default this is same
16429      * as the linked element , but could be changed to another element. Ex:
16430      * Roo.dd.DDProxy
16431      * @property dragElId
16432      * @type String
16433      * @private
16434      */
16435     dragElId: null,
16436
16437     /**
16438      * the id of the element that initiates the drag operation.  By default
16439      * this is the linked element, but could be changed to be a child of this
16440      * element.  This lets us do things like only starting the drag when the
16441      * header element within the linked html element is clicked.
16442      * @property handleElId
16443      * @type String
16444      * @private
16445      */
16446     handleElId: null,
16447
16448     /**
16449      * An associative array of HTML tags that will be ignored if clicked.
16450      * @property invalidHandleTypes
16451      * @type {string: string}
16452      */
16453     invalidHandleTypes: null,
16454
16455     /**
16456      * An associative array of ids for elements that will be ignored if clicked
16457      * @property invalidHandleIds
16458      * @type {string: string}
16459      */
16460     invalidHandleIds: null,
16461
16462     /**
16463      * An indexted array of css class names for elements that will be ignored
16464      * if clicked.
16465      * @property invalidHandleClasses
16466      * @type string[]
16467      */
16468     invalidHandleClasses: null,
16469
16470     /**
16471      * The linked element's absolute X position at the time the drag was
16472      * started
16473      * @property startPageX
16474      * @type int
16475      * @private
16476      */
16477     startPageX: 0,
16478
16479     /**
16480      * The linked element's absolute X position at the time the drag was
16481      * started
16482      * @property startPageY
16483      * @type int
16484      * @private
16485      */
16486     startPageY: 0,
16487
16488     /**
16489      * The group defines a logical collection of DragDrop objects that are
16490      * related.  Instances only get events when interacting with other
16491      * DragDrop object in the same group.  This lets us define multiple
16492      * groups using a single DragDrop subclass if we want.
16493      * @property groups
16494      * @type {string: string}
16495      */
16496     groups: null,
16497
16498     /**
16499      * Individual drag/drop instances can be locked.  This will prevent
16500      * onmousedown start drag.
16501      * @property locked
16502      * @type boolean
16503      * @private
16504      */
16505     locked: false,
16506
16507     /**
16508      * Lock this instance
16509      * @method lock
16510      */
16511     lock: function() { this.locked = true; },
16512
16513     /**
16514      * Unlock this instace
16515      * @method unlock
16516      */
16517     unlock: function() { this.locked = false; },
16518
16519     /**
16520      * By default, all insances can be a drop target.  This can be disabled by
16521      * setting isTarget to false.
16522      * @method isTarget
16523      * @type boolean
16524      */
16525     isTarget: true,
16526
16527     /**
16528      * The padding configured for this drag and drop object for calculating
16529      * the drop zone intersection with this object.
16530      * @method padding
16531      * @type int[]
16532      */
16533     padding: null,
16534
16535     /**
16536      * Cached reference to the linked element
16537      * @property _domRef
16538      * @private
16539      */
16540     _domRef: null,
16541
16542     /**
16543      * Internal typeof flag
16544      * @property __ygDragDrop
16545      * @private
16546      */
16547     __ygDragDrop: true,
16548
16549     /**
16550      * Set to true when horizontal contraints are applied
16551      * @property constrainX
16552      * @type boolean
16553      * @private
16554      */
16555     constrainX: false,
16556
16557     /**
16558      * Set to true when vertical contraints are applied
16559      * @property constrainY
16560      * @type boolean
16561      * @private
16562      */
16563     constrainY: false,
16564
16565     /**
16566      * The left constraint
16567      * @property minX
16568      * @type int
16569      * @private
16570      */
16571     minX: 0,
16572
16573     /**
16574      * The right constraint
16575      * @property maxX
16576      * @type int
16577      * @private
16578      */
16579     maxX: 0,
16580
16581     /**
16582      * The up constraint
16583      * @property minY
16584      * @type int
16585      * @type int
16586      * @private
16587      */
16588     minY: 0,
16589
16590     /**
16591      * The down constraint
16592      * @property maxY
16593      * @type int
16594      * @private
16595      */
16596     maxY: 0,
16597
16598     /**
16599      * Maintain offsets when we resetconstraints.  Set to true when you want
16600      * the position of the element relative to its parent to stay the same
16601      * when the page changes
16602      *
16603      * @property maintainOffset
16604      * @type boolean
16605      */
16606     maintainOffset: false,
16607
16608     /**
16609      * Array of pixel locations the element will snap to if we specified a
16610      * horizontal graduation/interval.  This array is generated automatically
16611      * when you define a tick interval.
16612      * @property xTicks
16613      * @type int[]
16614      */
16615     xTicks: null,
16616
16617     /**
16618      * Array of pixel locations the element will snap to if we specified a
16619      * vertical graduation/interval.  This array is generated automatically
16620      * when you define a tick interval.
16621      * @property yTicks
16622      * @type int[]
16623      */
16624     yTicks: null,
16625
16626     /**
16627      * By default the drag and drop instance will only respond to the primary
16628      * button click (left button for a right-handed mouse).  Set to true to
16629      * allow drag and drop to start with any mouse click that is propogated
16630      * by the browser
16631      * @property primaryButtonOnly
16632      * @type boolean
16633      */
16634     primaryButtonOnly: true,
16635
16636     /**
16637      * The availabe property is false until the linked dom element is accessible.
16638      * @property available
16639      * @type boolean
16640      */
16641     available: false,
16642
16643     /**
16644      * By default, drags can only be initiated if the mousedown occurs in the
16645      * region the linked element is.  This is done in part to work around a
16646      * bug in some browsers that mis-report the mousedown if the previous
16647      * mouseup happened outside of the window.  This property is set to true
16648      * if outer handles are defined.
16649      *
16650      * @property hasOuterHandles
16651      * @type boolean
16652      * @default false
16653      */
16654     hasOuterHandles: false,
16655
16656     /**
16657      * Code that executes immediately before the startDrag event
16658      * @method b4StartDrag
16659      * @private
16660      */
16661     b4StartDrag: function(x, y) { },
16662
16663     /**
16664      * Abstract method called after a drag/drop object is clicked
16665      * and the drag or mousedown time thresholds have beeen met.
16666      * @method startDrag
16667      * @param {int} X click location
16668      * @param {int} Y click location
16669      */
16670     startDrag: function(x, y) { /* override this */ },
16671
16672     /**
16673      * Code that executes immediately before the onDrag event
16674      * @method b4Drag
16675      * @private
16676      */
16677     b4Drag: function(e) { },
16678
16679     /**
16680      * Abstract method called during the onMouseMove event while dragging an
16681      * object.
16682      * @method onDrag
16683      * @param {Event} e the mousemove event
16684      */
16685     onDrag: function(e) { /* override this */ },
16686
16687     /**
16688      * Abstract method called when this element fist begins hovering over
16689      * another DragDrop obj
16690      * @method onDragEnter
16691      * @param {Event} e the mousemove event
16692      * @param {String|DragDrop[]} id In POINT mode, the element
16693      * id this is hovering over.  In INTERSECT mode, an array of one or more
16694      * dragdrop items being hovered over.
16695      */
16696     onDragEnter: function(e, id) { /* override this */ },
16697
16698     /**
16699      * Code that executes immediately before the onDragOver event
16700      * @method b4DragOver
16701      * @private
16702      */
16703     b4DragOver: function(e) { },
16704
16705     /**
16706      * Abstract method called when this element is hovering over another
16707      * DragDrop obj
16708      * @method onDragOver
16709      * @param {Event} e the mousemove event
16710      * @param {String|DragDrop[]} id In POINT mode, the element
16711      * id this is hovering over.  In INTERSECT mode, an array of dd items
16712      * being hovered over.
16713      */
16714     onDragOver: function(e, id) { /* override this */ },
16715
16716     /**
16717      * Code that executes immediately before the onDragOut event
16718      * @method b4DragOut
16719      * @private
16720      */
16721     b4DragOut: function(e) { },
16722
16723     /**
16724      * Abstract method called when we are no longer hovering over an element
16725      * @method onDragOut
16726      * @param {Event} e the mousemove event
16727      * @param {String|DragDrop[]} id In POINT mode, the element
16728      * id this was hovering over.  In INTERSECT mode, an array of dd items
16729      * that the mouse is no longer over.
16730      */
16731     onDragOut: function(e, id) { /* override this */ },
16732
16733     /**
16734      * Code that executes immediately before the onDragDrop event
16735      * @method b4DragDrop
16736      * @private
16737      */
16738     b4DragDrop: function(e) { },
16739
16740     /**
16741      * Abstract method called when this item is dropped on another DragDrop
16742      * obj
16743      * @method onDragDrop
16744      * @param {Event} e the mouseup event
16745      * @param {String|DragDrop[]} id In POINT mode, the element
16746      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16747      * was dropped on.
16748      */
16749     onDragDrop: function(e, id) { /* override this */ },
16750
16751     /**
16752      * Abstract method called when this item is dropped on an area with no
16753      * drop target
16754      * @method onInvalidDrop
16755      * @param {Event} e the mouseup event
16756      */
16757     onInvalidDrop: function(e) { /* override this */ },
16758
16759     /**
16760      * Code that executes immediately before the endDrag event
16761      * @method b4EndDrag
16762      * @private
16763      */
16764     b4EndDrag: function(e) { },
16765
16766     /**
16767      * Fired when we are done dragging the object
16768      * @method endDrag
16769      * @param {Event} e the mouseup event
16770      */
16771     endDrag: function(e) { /* override this */ },
16772
16773     /**
16774      * Code executed immediately before the onMouseDown event
16775      * @method b4MouseDown
16776      * @param {Event} e the mousedown event
16777      * @private
16778      */
16779     b4MouseDown: function(e) {  },
16780
16781     /**
16782      * Event handler that fires when a drag/drop obj gets a mousedown
16783      * @method onMouseDown
16784      * @param {Event} e the mousedown event
16785      */
16786     onMouseDown: function(e) { /* override this */ },
16787
16788     /**
16789      * Event handler that fires when a drag/drop obj gets a mouseup
16790      * @method onMouseUp
16791      * @param {Event} e the mouseup event
16792      */
16793     onMouseUp: function(e) { /* override this */ },
16794
16795     /**
16796      * Override the onAvailable method to do what is needed after the initial
16797      * position was determined.
16798      * @method onAvailable
16799      */
16800     onAvailable: function () {
16801     },
16802
16803     /*
16804      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16805      * @type Object
16806      */
16807     defaultPadding : {left:0, right:0, top:0, bottom:0},
16808
16809     /*
16810      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16811  *
16812  * Usage:
16813  <pre><code>
16814  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16815                 { dragElId: "existingProxyDiv" });
16816  dd.startDrag = function(){
16817      this.constrainTo("parent-id");
16818  };
16819  </code></pre>
16820  * Or you can initalize it using the {@link Roo.Element} object:
16821  <pre><code>
16822  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16823      startDrag : function(){
16824          this.constrainTo("parent-id");
16825      }
16826  });
16827  </code></pre>
16828      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16829      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16830      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16831      * an object containing the sides to pad. For example: {right:10, bottom:10}
16832      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16833      */
16834     constrainTo : function(constrainTo, pad, inContent){
16835         if(typeof pad == "number"){
16836             pad = {left: pad, right:pad, top:pad, bottom:pad};
16837         }
16838         pad = pad || this.defaultPadding;
16839         var b = Roo.get(this.getEl()).getBox();
16840         var ce = Roo.get(constrainTo);
16841         var s = ce.getScroll();
16842         var c, cd = ce.dom;
16843         if(cd == document.body){
16844             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16845         }else{
16846             xy = ce.getXY();
16847             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16848         }
16849
16850
16851         var topSpace = b.y - c.y;
16852         var leftSpace = b.x - c.x;
16853
16854         this.resetConstraints();
16855         this.setXConstraint(leftSpace - (pad.left||0), // left
16856                 c.width - leftSpace - b.width - (pad.right||0) //right
16857         );
16858         this.setYConstraint(topSpace - (pad.top||0), //top
16859                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16860         );
16861     },
16862
16863     /**
16864      * Returns a reference to the linked element
16865      * @method getEl
16866      * @return {HTMLElement} the html element
16867      */
16868     getEl: function() {
16869         if (!this._domRef) {
16870             this._domRef = Roo.getDom(this.id);
16871         }
16872
16873         return this._domRef;
16874     },
16875
16876     /**
16877      * Returns a reference to the actual element to drag.  By default this is
16878      * the same as the html element, but it can be assigned to another
16879      * element. An example of this can be found in Roo.dd.DDProxy
16880      * @method getDragEl
16881      * @return {HTMLElement} the html element
16882      */
16883     getDragEl: function() {
16884         return Roo.getDom(this.dragElId);
16885     },
16886
16887     /**
16888      * Sets up the DragDrop object.  Must be called in the constructor of any
16889      * Roo.dd.DragDrop subclass
16890      * @method init
16891      * @param id the id of the linked element
16892      * @param {String} sGroup the group of related items
16893      * @param {object} config configuration attributes
16894      */
16895     init: function(id, sGroup, config) {
16896         this.initTarget(id, sGroup, config);
16897         if (!Roo.isTouch) {
16898             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16899         }
16900         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16901         // Event.on(this.id, "selectstart", Event.preventDefault);
16902     },
16903
16904     /**
16905      * Initializes Targeting functionality only... the object does not
16906      * get a mousedown handler.
16907      * @method initTarget
16908      * @param id the id of the linked element
16909      * @param {String} sGroup the group of related items
16910      * @param {object} config configuration attributes
16911      */
16912     initTarget: function(id, sGroup, config) {
16913
16914         // configuration attributes
16915         this.config = config || {};
16916
16917         // create a local reference to the drag and drop manager
16918         this.DDM = Roo.dd.DDM;
16919         // initialize the groups array
16920         this.groups = {};
16921
16922         // assume that we have an element reference instead of an id if the
16923         // parameter is not a string
16924         if (typeof id !== "string") {
16925             id = Roo.id(id);
16926         }
16927
16928         // set the id
16929         this.id = id;
16930
16931         // add to an interaction group
16932         this.addToGroup((sGroup) ? sGroup : "default");
16933
16934         // We don't want to register this as the handle with the manager
16935         // so we just set the id rather than calling the setter.
16936         this.handleElId = id;
16937
16938         // the linked element is the element that gets dragged by default
16939         this.setDragElId(id);
16940
16941         // by default, clicked anchors will not start drag operations.
16942         this.invalidHandleTypes = { A: "A" };
16943         this.invalidHandleIds = {};
16944         this.invalidHandleClasses = [];
16945
16946         this.applyConfig();
16947
16948         this.handleOnAvailable();
16949     },
16950
16951     /**
16952      * Applies the configuration parameters that were passed into the constructor.
16953      * This is supposed to happen at each level through the inheritance chain.  So
16954      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16955      * DragDrop in order to get all of the parameters that are available in
16956      * each object.
16957      * @method applyConfig
16958      */
16959     applyConfig: function() {
16960
16961         // configurable properties:
16962         //    padding, isTarget, maintainOffset, primaryButtonOnly
16963         this.padding           = this.config.padding || [0, 0, 0, 0];
16964         this.isTarget          = (this.config.isTarget !== false);
16965         this.maintainOffset    = (this.config.maintainOffset);
16966         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16967
16968     },
16969
16970     /**
16971      * Executed when the linked element is available
16972      * @method handleOnAvailable
16973      * @private
16974      */
16975     handleOnAvailable: function() {
16976         this.available = true;
16977         this.resetConstraints();
16978         this.onAvailable();
16979     },
16980
16981      /**
16982      * Configures the padding for the target zone in px.  Effectively expands
16983      * (or reduces) the virtual object size for targeting calculations.
16984      * Supports css-style shorthand; if only one parameter is passed, all sides
16985      * will have that padding, and if only two are passed, the top and bottom
16986      * will have the first param, the left and right the second.
16987      * @method setPadding
16988      * @param {int} iTop    Top pad
16989      * @param {int} iRight  Right pad
16990      * @param {int} iBot    Bot pad
16991      * @param {int} iLeft   Left pad
16992      */
16993     setPadding: function(iTop, iRight, iBot, iLeft) {
16994         // this.padding = [iLeft, iRight, iTop, iBot];
16995         if (!iRight && 0 !== iRight) {
16996             this.padding = [iTop, iTop, iTop, iTop];
16997         } else if (!iBot && 0 !== iBot) {
16998             this.padding = [iTop, iRight, iTop, iRight];
16999         } else {
17000             this.padding = [iTop, iRight, iBot, iLeft];
17001         }
17002     },
17003
17004     /**
17005      * Stores the initial placement of the linked element.
17006      * @method setInitialPosition
17007      * @param {int} diffX   the X offset, default 0
17008      * @param {int} diffY   the Y offset, default 0
17009      */
17010     setInitPosition: function(diffX, diffY) {
17011         var el = this.getEl();
17012
17013         if (!this.DDM.verifyEl(el)) {
17014             return;
17015         }
17016
17017         var dx = diffX || 0;
17018         var dy = diffY || 0;
17019
17020         var p = Dom.getXY( el );
17021
17022         this.initPageX = p[0] - dx;
17023         this.initPageY = p[1] - dy;
17024
17025         this.lastPageX = p[0];
17026         this.lastPageY = p[1];
17027
17028
17029         this.setStartPosition(p);
17030     },
17031
17032     /**
17033      * Sets the start position of the element.  This is set when the obj
17034      * is initialized, the reset when a drag is started.
17035      * @method setStartPosition
17036      * @param pos current position (from previous lookup)
17037      * @private
17038      */
17039     setStartPosition: function(pos) {
17040         var p = pos || Dom.getXY( this.getEl() );
17041         this.deltaSetXY = null;
17042
17043         this.startPageX = p[0];
17044         this.startPageY = p[1];
17045     },
17046
17047     /**
17048      * Add this instance to a group of related drag/drop objects.  All
17049      * instances belong to at least one group, and can belong to as many
17050      * groups as needed.
17051      * @method addToGroup
17052      * @param sGroup {string} the name of the group
17053      */
17054     addToGroup: function(sGroup) {
17055         this.groups[sGroup] = true;
17056         this.DDM.regDragDrop(this, sGroup);
17057     },
17058
17059     /**
17060      * Remove's this instance from the supplied interaction group
17061      * @method removeFromGroup
17062      * @param {string}  sGroup  The group to drop
17063      */
17064     removeFromGroup: function(sGroup) {
17065         if (this.groups[sGroup]) {
17066             delete this.groups[sGroup];
17067         }
17068
17069         this.DDM.removeDDFromGroup(this, sGroup);
17070     },
17071
17072     /**
17073      * Allows you to specify that an element other than the linked element
17074      * will be moved with the cursor during a drag
17075      * @method setDragElId
17076      * @param id {string} the id of the element that will be used to initiate the drag
17077      */
17078     setDragElId: function(id) {
17079         this.dragElId = id;
17080     },
17081
17082     /**
17083      * Allows you to specify a child of the linked element that should be
17084      * used to initiate the drag operation.  An example of this would be if
17085      * you have a content div with text and links.  Clicking anywhere in the
17086      * content area would normally start the drag operation.  Use this method
17087      * to specify that an element inside of the content div is the element
17088      * that starts the drag operation.
17089      * @method setHandleElId
17090      * @param id {string} the id of the element that will be used to
17091      * initiate the drag.
17092      */
17093     setHandleElId: function(id) {
17094         if (typeof id !== "string") {
17095             id = Roo.id(id);
17096         }
17097         this.handleElId = id;
17098         this.DDM.regHandle(this.id, id);
17099     },
17100
17101     /**
17102      * Allows you to set an element outside of the linked element as a drag
17103      * handle
17104      * @method setOuterHandleElId
17105      * @param id the id of the element that will be used to initiate the drag
17106      */
17107     setOuterHandleElId: function(id) {
17108         if (typeof id !== "string") {
17109             id = Roo.id(id);
17110         }
17111         Event.on(id, "mousedown",
17112                 this.handleMouseDown, this);
17113         this.setHandleElId(id);
17114
17115         this.hasOuterHandles = true;
17116     },
17117
17118     /**
17119      * Remove all drag and drop hooks for this element
17120      * @method unreg
17121      */
17122     unreg: function() {
17123         Event.un(this.id, "mousedown",
17124                 this.handleMouseDown);
17125         Event.un(this.id, "touchstart",
17126                 this.handleMouseDown);
17127         this._domRef = null;
17128         this.DDM._remove(this);
17129     },
17130
17131     destroy : function(){
17132         this.unreg();
17133     },
17134
17135     /**
17136      * Returns true if this instance is locked, or the drag drop mgr is locked
17137      * (meaning that all drag/drop is disabled on the page.)
17138      * @method isLocked
17139      * @return {boolean} true if this obj or all drag/drop is locked, else
17140      * false
17141      */
17142     isLocked: function() {
17143         return (this.DDM.isLocked() || this.locked);
17144     },
17145
17146     /**
17147      * Fired when this object is clicked
17148      * @method handleMouseDown
17149      * @param {Event} e
17150      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17151      * @private
17152      */
17153     handleMouseDown: function(e, oDD){
17154      
17155         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17156             //Roo.log('not touch/ button !=0');
17157             return;
17158         }
17159         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17160             return; // double touch..
17161         }
17162         
17163
17164         if (this.isLocked()) {
17165             //Roo.log('locked');
17166             return;
17167         }
17168
17169         this.DDM.refreshCache(this.groups);
17170 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17171         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17172         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17173             //Roo.log('no outer handes or not over target');
17174                 // do nothing.
17175         } else {
17176 //            Roo.log('check validator');
17177             if (this.clickValidator(e)) {
17178 //                Roo.log('validate success');
17179                 // set the initial element position
17180                 this.setStartPosition();
17181
17182
17183                 this.b4MouseDown(e);
17184                 this.onMouseDown(e);
17185
17186                 this.DDM.handleMouseDown(e, this);
17187
17188                 this.DDM.stopEvent(e);
17189             } else {
17190
17191
17192             }
17193         }
17194     },
17195
17196     clickValidator: function(e) {
17197         var target = e.getTarget();
17198         return ( this.isValidHandleChild(target) &&
17199                     (this.id == this.handleElId ||
17200                         this.DDM.handleWasClicked(target, this.id)) );
17201     },
17202
17203     /**
17204      * Allows you to specify a tag name that should not start a drag operation
17205      * when clicked.  This is designed to facilitate embedding links within a
17206      * drag handle that do something other than start the drag.
17207      * @method addInvalidHandleType
17208      * @param {string} tagName the type of element to exclude
17209      */
17210     addInvalidHandleType: function(tagName) {
17211         var type = tagName.toUpperCase();
17212         this.invalidHandleTypes[type] = type;
17213     },
17214
17215     /**
17216      * Lets you to specify an element id for a child of a drag handle
17217      * that should not initiate a drag
17218      * @method addInvalidHandleId
17219      * @param {string} id the element id of the element you wish to ignore
17220      */
17221     addInvalidHandleId: function(id) {
17222         if (typeof id !== "string") {
17223             id = Roo.id(id);
17224         }
17225         this.invalidHandleIds[id] = id;
17226     },
17227
17228     /**
17229      * Lets you specify a css class of elements that will not initiate a drag
17230      * @method addInvalidHandleClass
17231      * @param {string} cssClass the class of the elements you wish to ignore
17232      */
17233     addInvalidHandleClass: function(cssClass) {
17234         this.invalidHandleClasses.push(cssClass);
17235     },
17236
17237     /**
17238      * Unsets an excluded tag name set by addInvalidHandleType
17239      * @method removeInvalidHandleType
17240      * @param {string} tagName the type of element to unexclude
17241      */
17242     removeInvalidHandleType: function(tagName) {
17243         var type = tagName.toUpperCase();
17244         // this.invalidHandleTypes[type] = null;
17245         delete this.invalidHandleTypes[type];
17246     },
17247
17248     /**
17249      * Unsets an invalid handle id
17250      * @method removeInvalidHandleId
17251      * @param {string} id the id of the element to re-enable
17252      */
17253     removeInvalidHandleId: function(id) {
17254         if (typeof id !== "string") {
17255             id = Roo.id(id);
17256         }
17257         delete this.invalidHandleIds[id];
17258     },
17259
17260     /**
17261      * Unsets an invalid css class
17262      * @method removeInvalidHandleClass
17263      * @param {string} cssClass the class of the element(s) you wish to
17264      * re-enable
17265      */
17266     removeInvalidHandleClass: function(cssClass) {
17267         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17268             if (this.invalidHandleClasses[i] == cssClass) {
17269                 delete this.invalidHandleClasses[i];
17270             }
17271         }
17272     },
17273
17274     /**
17275      * Checks the tag exclusion list to see if this click should be ignored
17276      * @method isValidHandleChild
17277      * @param {HTMLElement} node the HTMLElement to evaluate
17278      * @return {boolean} true if this is a valid tag type, false if not
17279      */
17280     isValidHandleChild: function(node) {
17281
17282         var valid = true;
17283         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17284         var nodeName;
17285         try {
17286             nodeName = node.nodeName.toUpperCase();
17287         } catch(e) {
17288             nodeName = node.nodeName;
17289         }
17290         valid = valid && !this.invalidHandleTypes[nodeName];
17291         valid = valid && !this.invalidHandleIds[node.id];
17292
17293         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17294             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17295         }
17296
17297
17298         return valid;
17299
17300     },
17301
17302     /**
17303      * Create the array of horizontal tick marks if an interval was specified
17304      * in setXConstraint().
17305      * @method setXTicks
17306      * @private
17307      */
17308     setXTicks: function(iStartX, iTickSize) {
17309         this.xTicks = [];
17310         this.xTickSize = iTickSize;
17311
17312         var tickMap = {};
17313
17314         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17315             if (!tickMap[i]) {
17316                 this.xTicks[this.xTicks.length] = i;
17317                 tickMap[i] = true;
17318             }
17319         }
17320
17321         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17322             if (!tickMap[i]) {
17323                 this.xTicks[this.xTicks.length] = i;
17324                 tickMap[i] = true;
17325             }
17326         }
17327
17328         this.xTicks.sort(this.DDM.numericSort) ;
17329     },
17330
17331     /**
17332      * Create the array of vertical tick marks if an interval was specified in
17333      * setYConstraint().
17334      * @method setYTicks
17335      * @private
17336      */
17337     setYTicks: function(iStartY, iTickSize) {
17338         this.yTicks = [];
17339         this.yTickSize = iTickSize;
17340
17341         var tickMap = {};
17342
17343         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17344             if (!tickMap[i]) {
17345                 this.yTicks[this.yTicks.length] = i;
17346                 tickMap[i] = true;
17347             }
17348         }
17349
17350         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17351             if (!tickMap[i]) {
17352                 this.yTicks[this.yTicks.length] = i;
17353                 tickMap[i] = true;
17354             }
17355         }
17356
17357         this.yTicks.sort(this.DDM.numericSort) ;
17358     },
17359
17360     /**
17361      * By default, the element can be dragged any place on the screen.  Use
17362      * this method to limit the horizontal travel of the element.  Pass in
17363      * 0,0 for the parameters if you want to lock the drag to the y axis.
17364      * @method setXConstraint
17365      * @param {int} iLeft the number of pixels the element can move to the left
17366      * @param {int} iRight the number of pixels the element can move to the
17367      * right
17368      * @param {int} iTickSize optional parameter for specifying that the
17369      * element
17370      * should move iTickSize pixels at a time.
17371      */
17372     setXConstraint: function(iLeft, iRight, iTickSize) {
17373         this.leftConstraint = iLeft;
17374         this.rightConstraint = iRight;
17375
17376         this.minX = this.initPageX - iLeft;
17377         this.maxX = this.initPageX + iRight;
17378         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17379
17380         this.constrainX = true;
17381     },
17382
17383     /**
17384      * Clears any constraints applied to this instance.  Also clears ticks
17385      * since they can't exist independent of a constraint at this time.
17386      * @method clearConstraints
17387      */
17388     clearConstraints: function() {
17389         this.constrainX = false;
17390         this.constrainY = false;
17391         this.clearTicks();
17392     },
17393
17394     /**
17395      * Clears any tick interval defined for this instance
17396      * @method clearTicks
17397      */
17398     clearTicks: function() {
17399         this.xTicks = null;
17400         this.yTicks = null;
17401         this.xTickSize = 0;
17402         this.yTickSize = 0;
17403     },
17404
17405     /**
17406      * By default, the element can be dragged any place on the screen.  Set
17407      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17408      * parameters if you want to lock the drag to the x axis.
17409      * @method setYConstraint
17410      * @param {int} iUp the number of pixels the element can move up
17411      * @param {int} iDown the number of pixels the element can move down
17412      * @param {int} iTickSize optional parameter for specifying that the
17413      * element should move iTickSize pixels at a time.
17414      */
17415     setYConstraint: function(iUp, iDown, iTickSize) {
17416         this.topConstraint = iUp;
17417         this.bottomConstraint = iDown;
17418
17419         this.minY = this.initPageY - iUp;
17420         this.maxY = this.initPageY + iDown;
17421         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17422
17423         this.constrainY = true;
17424
17425     },
17426
17427     /**
17428      * resetConstraints must be called if you manually reposition a dd element.
17429      * @method resetConstraints
17430      * @param {boolean} maintainOffset
17431      */
17432     resetConstraints: function() {
17433
17434
17435         // Maintain offsets if necessary
17436         if (this.initPageX || this.initPageX === 0) {
17437             // figure out how much this thing has moved
17438             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17439             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17440
17441             this.setInitPosition(dx, dy);
17442
17443         // This is the first time we have detected the element's position
17444         } else {
17445             this.setInitPosition();
17446         }
17447
17448         if (this.constrainX) {
17449             this.setXConstraint( this.leftConstraint,
17450                                  this.rightConstraint,
17451                                  this.xTickSize        );
17452         }
17453
17454         if (this.constrainY) {
17455             this.setYConstraint( this.topConstraint,
17456                                  this.bottomConstraint,
17457                                  this.yTickSize         );
17458         }
17459     },
17460
17461     /**
17462      * Normally the drag element is moved pixel by pixel, but we can specify
17463      * that it move a number of pixels at a time.  This method resolves the
17464      * location when we have it set up like this.
17465      * @method getTick
17466      * @param {int} val where we want to place the object
17467      * @param {int[]} tickArray sorted array of valid points
17468      * @return {int} the closest tick
17469      * @private
17470      */
17471     getTick: function(val, tickArray) {
17472
17473         if (!tickArray) {
17474             // If tick interval is not defined, it is effectively 1 pixel,
17475             // so we return the value passed to us.
17476             return val;
17477         } else if (tickArray[0] >= val) {
17478             // The value is lower than the first tick, so we return the first
17479             // tick.
17480             return tickArray[0];
17481         } else {
17482             for (var i=0, len=tickArray.length; i<len; ++i) {
17483                 var next = i + 1;
17484                 if (tickArray[next] && tickArray[next] >= val) {
17485                     var diff1 = val - tickArray[i];
17486                     var diff2 = tickArray[next] - val;
17487                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17488                 }
17489             }
17490
17491             // The value is larger than the last tick, so we return the last
17492             // tick.
17493             return tickArray[tickArray.length - 1];
17494         }
17495     },
17496
17497     /**
17498      * toString method
17499      * @method toString
17500      * @return {string} string representation of the dd obj
17501      */
17502     toString: function() {
17503         return ("DragDrop " + this.id);
17504     }
17505
17506 });
17507
17508 })();
17509 /*
17510  * Based on:
17511  * Ext JS Library 1.1.1
17512  * Copyright(c) 2006-2007, Ext JS, LLC.
17513  *
17514  * Originally Released Under LGPL - original licence link has changed is not relivant.
17515  *
17516  * Fork - LGPL
17517  * <script type="text/javascript">
17518  */
17519
17520
17521 /**
17522  * The drag and drop utility provides a framework for building drag and drop
17523  * applications.  In addition to enabling drag and drop for specific elements,
17524  * the drag and drop elements are tracked by the manager class, and the
17525  * interactions between the various elements are tracked during the drag and
17526  * the implementing code is notified about these important moments.
17527  */
17528
17529 // Only load the library once.  Rewriting the manager class would orphan
17530 // existing drag and drop instances.
17531 if (!Roo.dd.DragDropMgr) {
17532
17533 /**
17534  * @class Roo.dd.DragDropMgr
17535  * DragDropMgr is a singleton that tracks the element interaction for
17536  * all DragDrop items in the window.  Generally, you will not call
17537  * this class directly, but it does have helper methods that could
17538  * be useful in your DragDrop implementations.
17539  * @singleton
17540  */
17541 Roo.dd.DragDropMgr = function() {
17542
17543     var Event = Roo.EventManager;
17544
17545     return {
17546
17547         /**
17548          * Two dimensional Array of registered DragDrop objects.  The first
17549          * dimension is the DragDrop item group, the second the DragDrop
17550          * object.
17551          * @property ids
17552          * @type {string: string}
17553          * @private
17554          * @static
17555          */
17556         ids: {},
17557
17558         /**
17559          * Array of element ids defined as drag handles.  Used to determine
17560          * if the element that generated the mousedown event is actually the
17561          * handle and not the html element itself.
17562          * @property handleIds
17563          * @type {string: string}
17564          * @private
17565          * @static
17566          */
17567         handleIds: {},
17568
17569         /**
17570          * the DragDrop object that is currently being dragged
17571          * @property dragCurrent
17572          * @type DragDrop
17573          * @private
17574          * @static
17575          **/
17576         dragCurrent: null,
17577
17578         /**
17579          * the DragDrop object(s) that are being hovered over
17580          * @property dragOvers
17581          * @type Array
17582          * @private
17583          * @static
17584          */
17585         dragOvers: {},
17586
17587         /**
17588          * the X distance between the cursor and the object being dragged
17589          * @property deltaX
17590          * @type int
17591          * @private
17592          * @static
17593          */
17594         deltaX: 0,
17595
17596         /**
17597          * the Y distance between the cursor and the object being dragged
17598          * @property deltaY
17599          * @type int
17600          * @private
17601          * @static
17602          */
17603         deltaY: 0,
17604
17605         /**
17606          * Flag to determine if we should prevent the default behavior of the
17607          * events we define. By default this is true, but this can be set to
17608          * false if you need the default behavior (not recommended)
17609          * @property preventDefault
17610          * @type boolean
17611          * @static
17612          */
17613         preventDefault: true,
17614
17615         /**
17616          * Flag to determine if we should stop the propagation of the events
17617          * we generate. This is true by default but you may want to set it to
17618          * false if the html element contains other features that require the
17619          * mouse click.
17620          * @property stopPropagation
17621          * @type boolean
17622          * @static
17623          */
17624         stopPropagation: true,
17625
17626         /**
17627          * Internal flag that is set to true when drag and drop has been
17628          * intialized
17629          * @property initialized
17630          * @private
17631          * @static
17632          */
17633         initalized: false,
17634
17635         /**
17636          * All drag and drop can be disabled.
17637          * @property locked
17638          * @private
17639          * @static
17640          */
17641         locked: false,
17642
17643         /**
17644          * Called the first time an element is registered.
17645          * @method init
17646          * @private
17647          * @static
17648          */
17649         init: function() {
17650             this.initialized = true;
17651         },
17652
17653         /**
17654          * In point mode, drag and drop interaction is defined by the
17655          * location of the cursor during the drag/drop
17656          * @property POINT
17657          * @type int
17658          * @static
17659          */
17660         POINT: 0,
17661
17662         /**
17663          * In intersect mode, drag and drop interactio nis defined by the
17664          * overlap of two or more drag and drop objects.
17665          * @property INTERSECT
17666          * @type int
17667          * @static
17668          */
17669         INTERSECT: 1,
17670
17671         /**
17672          * The current drag and drop mode.  Default: POINT
17673          * @property mode
17674          * @type int
17675          * @static
17676          */
17677         mode: 0,
17678
17679         /**
17680          * Runs method on all drag and drop objects
17681          * @method _execOnAll
17682          * @private
17683          * @static
17684          */
17685         _execOnAll: function(sMethod, args) {
17686             for (var i in this.ids) {
17687                 for (var j in this.ids[i]) {
17688                     var oDD = this.ids[i][j];
17689                     if (! this.isTypeOfDD(oDD)) {
17690                         continue;
17691                     }
17692                     oDD[sMethod].apply(oDD, args);
17693                 }
17694             }
17695         },
17696
17697         /**
17698          * Drag and drop initialization.  Sets up the global event handlers
17699          * @method _onLoad
17700          * @private
17701          * @static
17702          */
17703         _onLoad: function() {
17704
17705             this.init();
17706
17707             if (!Roo.isTouch) {
17708                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17709                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17710             }
17711             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17712             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17713             
17714             Event.on(window,   "unload",    this._onUnload, this, true);
17715             Event.on(window,   "resize",    this._onResize, this, true);
17716             // Event.on(window,   "mouseout",    this._test);
17717
17718         },
17719
17720         /**
17721          * Reset constraints on all drag and drop objs
17722          * @method _onResize
17723          * @private
17724          * @static
17725          */
17726         _onResize: function(e) {
17727             this._execOnAll("resetConstraints", []);
17728         },
17729
17730         /**
17731          * Lock all drag and drop functionality
17732          * @method lock
17733          * @static
17734          */
17735         lock: function() { this.locked = true; },
17736
17737         /**
17738          * Unlock all drag and drop functionality
17739          * @method unlock
17740          * @static
17741          */
17742         unlock: function() { this.locked = false; },
17743
17744         /**
17745          * Is drag and drop locked?
17746          * @method isLocked
17747          * @return {boolean} True if drag and drop is locked, false otherwise.
17748          * @static
17749          */
17750         isLocked: function() { return this.locked; },
17751
17752         /**
17753          * Location cache that is set for all drag drop objects when a drag is
17754          * initiated, cleared when the drag is finished.
17755          * @property locationCache
17756          * @private
17757          * @static
17758          */
17759         locationCache: {},
17760
17761         /**
17762          * Set useCache to false if you want to force object the lookup of each
17763          * drag and drop linked element constantly during a drag.
17764          * @property useCache
17765          * @type boolean
17766          * @static
17767          */
17768         useCache: true,
17769
17770         /**
17771          * The number of pixels that the mouse needs to move after the
17772          * mousedown before the drag is initiated.  Default=3;
17773          * @property clickPixelThresh
17774          * @type int
17775          * @static
17776          */
17777         clickPixelThresh: 3,
17778
17779         /**
17780          * The number of milliseconds after the mousedown event to initiate the
17781          * drag if we don't get a mouseup event. Default=1000
17782          * @property clickTimeThresh
17783          * @type int
17784          * @static
17785          */
17786         clickTimeThresh: 350,
17787
17788         /**
17789          * Flag that indicates that either the drag pixel threshold or the
17790          * mousdown time threshold has been met
17791          * @property dragThreshMet
17792          * @type boolean
17793          * @private
17794          * @static
17795          */
17796         dragThreshMet: false,
17797
17798         /**
17799          * Timeout used for the click time threshold
17800          * @property clickTimeout
17801          * @type Object
17802          * @private
17803          * @static
17804          */
17805         clickTimeout: null,
17806
17807         /**
17808          * The X position of the mousedown event stored for later use when a
17809          * drag threshold is met.
17810          * @property startX
17811          * @type int
17812          * @private
17813          * @static
17814          */
17815         startX: 0,
17816
17817         /**
17818          * The Y position of the mousedown event stored for later use when a
17819          * drag threshold is met.
17820          * @property startY
17821          * @type int
17822          * @private
17823          * @static
17824          */
17825         startY: 0,
17826
17827         /**
17828          * Each DragDrop instance must be registered with the DragDropMgr.
17829          * This is executed in DragDrop.init()
17830          * @method regDragDrop
17831          * @param {DragDrop} oDD the DragDrop object to register
17832          * @param {String} sGroup the name of the group this element belongs to
17833          * @static
17834          */
17835         regDragDrop: function(oDD, sGroup) {
17836             if (!this.initialized) { this.init(); }
17837
17838             if (!this.ids[sGroup]) {
17839                 this.ids[sGroup] = {};
17840             }
17841             this.ids[sGroup][oDD.id] = oDD;
17842         },
17843
17844         /**
17845          * Removes the supplied dd instance from the supplied group. Executed
17846          * by DragDrop.removeFromGroup, so don't call this function directly.
17847          * @method removeDDFromGroup
17848          * @private
17849          * @static
17850          */
17851         removeDDFromGroup: function(oDD, sGroup) {
17852             if (!this.ids[sGroup]) {
17853                 this.ids[sGroup] = {};
17854             }
17855
17856             var obj = this.ids[sGroup];
17857             if (obj && obj[oDD.id]) {
17858                 delete obj[oDD.id];
17859             }
17860         },
17861
17862         /**
17863          * Unregisters a drag and drop item.  This is executed in
17864          * DragDrop.unreg, use that method instead of calling this directly.
17865          * @method _remove
17866          * @private
17867          * @static
17868          */
17869         _remove: function(oDD) {
17870             for (var g in oDD.groups) {
17871                 if (g && this.ids[g][oDD.id]) {
17872                     delete this.ids[g][oDD.id];
17873                 }
17874             }
17875             delete this.handleIds[oDD.id];
17876         },
17877
17878         /**
17879          * Each DragDrop handle element must be registered.  This is done
17880          * automatically when executing DragDrop.setHandleElId()
17881          * @method regHandle
17882          * @param {String} sDDId the DragDrop id this element is a handle for
17883          * @param {String} sHandleId the id of the element that is the drag
17884          * handle
17885          * @static
17886          */
17887         regHandle: function(sDDId, sHandleId) {
17888             if (!this.handleIds[sDDId]) {
17889                 this.handleIds[sDDId] = {};
17890             }
17891             this.handleIds[sDDId][sHandleId] = sHandleId;
17892         },
17893
17894         /**
17895          * Utility function to determine if a given element has been
17896          * registered as a drag drop item.
17897          * @method isDragDrop
17898          * @param {String} id the element id to check
17899          * @return {boolean} true if this element is a DragDrop item,
17900          * false otherwise
17901          * @static
17902          */
17903         isDragDrop: function(id) {
17904             return ( this.getDDById(id) ) ? true : false;
17905         },
17906
17907         /**
17908          * Returns the drag and drop instances that are in all groups the
17909          * passed in instance belongs to.
17910          * @method getRelated
17911          * @param {DragDrop} p_oDD the obj to get related data for
17912          * @param {boolean} bTargetsOnly if true, only return targetable objs
17913          * @return {DragDrop[]} the related instances
17914          * @static
17915          */
17916         getRelated: function(p_oDD, bTargetsOnly) {
17917             var oDDs = [];
17918             for (var i in p_oDD.groups) {
17919                 for (j in this.ids[i]) {
17920                     var dd = this.ids[i][j];
17921                     if (! this.isTypeOfDD(dd)) {
17922                         continue;
17923                     }
17924                     if (!bTargetsOnly || dd.isTarget) {
17925                         oDDs[oDDs.length] = dd;
17926                     }
17927                 }
17928             }
17929
17930             return oDDs;
17931         },
17932
17933         /**
17934          * Returns true if the specified dd target is a legal target for
17935          * the specifice drag obj
17936          * @method isLegalTarget
17937          * @param {DragDrop} the drag obj
17938          * @param {DragDrop} the target
17939          * @return {boolean} true if the target is a legal target for the
17940          * dd obj
17941          * @static
17942          */
17943         isLegalTarget: function (oDD, oTargetDD) {
17944             var targets = this.getRelated(oDD, true);
17945             for (var i=0, len=targets.length;i<len;++i) {
17946                 if (targets[i].id == oTargetDD.id) {
17947                     return true;
17948                 }
17949             }
17950
17951             return false;
17952         },
17953
17954         /**
17955          * My goal is to be able to transparently determine if an object is
17956          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17957          * returns "object", oDD.constructor.toString() always returns
17958          * "DragDrop" and not the name of the subclass.  So for now it just
17959          * evaluates a well-known variable in DragDrop.
17960          * @method isTypeOfDD
17961          * @param {Object} the object to evaluate
17962          * @return {boolean} true if typeof oDD = DragDrop
17963          * @static
17964          */
17965         isTypeOfDD: function (oDD) {
17966             return (oDD && oDD.__ygDragDrop);
17967         },
17968
17969         /**
17970          * Utility function to determine if a given element has been
17971          * registered as a drag drop handle for the given Drag Drop object.
17972          * @method isHandle
17973          * @param {String} id the element id to check
17974          * @return {boolean} true if this element is a DragDrop handle, false
17975          * otherwise
17976          * @static
17977          */
17978         isHandle: function(sDDId, sHandleId) {
17979             return ( this.handleIds[sDDId] &&
17980                             this.handleIds[sDDId][sHandleId] );
17981         },
17982
17983         /**
17984          * Returns the DragDrop instance for a given id
17985          * @method getDDById
17986          * @param {String} id the id of the DragDrop object
17987          * @return {DragDrop} the drag drop object, null if it is not found
17988          * @static
17989          */
17990         getDDById: function(id) {
17991             for (var i in this.ids) {
17992                 if (this.ids[i][id]) {
17993                     return this.ids[i][id];
17994                 }
17995             }
17996             return null;
17997         },
17998
17999         /**
18000          * Fired after a registered DragDrop object gets the mousedown event.
18001          * Sets up the events required to track the object being dragged
18002          * @method handleMouseDown
18003          * @param {Event} e the event
18004          * @param oDD the DragDrop object being dragged
18005          * @private
18006          * @static
18007          */
18008         handleMouseDown: function(e, oDD) {
18009             if(Roo.QuickTips){
18010                 Roo.QuickTips.disable();
18011             }
18012             this.currentTarget = e.getTarget();
18013
18014             this.dragCurrent = oDD;
18015
18016             var el = oDD.getEl();
18017
18018             // track start position
18019             this.startX = e.getPageX();
18020             this.startY = e.getPageY();
18021
18022             this.deltaX = this.startX - el.offsetLeft;
18023             this.deltaY = this.startY - el.offsetTop;
18024
18025             this.dragThreshMet = false;
18026
18027             this.clickTimeout = setTimeout(
18028                     function() {
18029                         var DDM = Roo.dd.DDM;
18030                         DDM.startDrag(DDM.startX, DDM.startY);
18031                     },
18032                     this.clickTimeThresh );
18033         },
18034
18035         /**
18036          * Fired when either the drag pixel threshol or the mousedown hold
18037          * time threshold has been met.
18038          * @method startDrag
18039          * @param x {int} the X position of the original mousedown
18040          * @param y {int} the Y position of the original mousedown
18041          * @static
18042          */
18043         startDrag: function(x, y) {
18044             clearTimeout(this.clickTimeout);
18045             if (this.dragCurrent) {
18046                 this.dragCurrent.b4StartDrag(x, y);
18047                 this.dragCurrent.startDrag(x, y);
18048             }
18049             this.dragThreshMet = true;
18050         },
18051
18052         /**
18053          * Internal function to handle the mouseup event.  Will be invoked
18054          * from the context of the document.
18055          * @method handleMouseUp
18056          * @param {Event} e the event
18057          * @private
18058          * @static
18059          */
18060         handleMouseUp: function(e) {
18061
18062             if(Roo.QuickTips){
18063                 Roo.QuickTips.enable();
18064             }
18065             if (! this.dragCurrent) {
18066                 return;
18067             }
18068
18069             clearTimeout(this.clickTimeout);
18070
18071             if (this.dragThreshMet) {
18072                 this.fireEvents(e, true);
18073             } else {
18074             }
18075
18076             this.stopDrag(e);
18077
18078             this.stopEvent(e);
18079         },
18080
18081         /**
18082          * Utility to stop event propagation and event default, if these
18083          * features are turned on.
18084          * @method stopEvent
18085          * @param {Event} e the event as returned by this.getEvent()
18086          * @static
18087          */
18088         stopEvent: function(e){
18089             if(this.stopPropagation) {
18090                 e.stopPropagation();
18091             }
18092
18093             if (this.preventDefault) {
18094                 e.preventDefault();
18095             }
18096         },
18097
18098         /**
18099          * Internal function to clean up event handlers after the drag
18100          * operation is complete
18101          * @method stopDrag
18102          * @param {Event} e the event
18103          * @private
18104          * @static
18105          */
18106         stopDrag: function(e) {
18107             // Fire the drag end event for the item that was dragged
18108             if (this.dragCurrent) {
18109                 if (this.dragThreshMet) {
18110                     this.dragCurrent.b4EndDrag(e);
18111                     this.dragCurrent.endDrag(e);
18112                 }
18113
18114                 this.dragCurrent.onMouseUp(e);
18115             }
18116
18117             this.dragCurrent = null;
18118             this.dragOvers = {};
18119         },
18120
18121         /**
18122          * Internal function to handle the mousemove event.  Will be invoked
18123          * from the context of the html element.
18124          *
18125          * @TODO figure out what we can do about mouse events lost when the
18126          * user drags objects beyond the window boundary.  Currently we can
18127          * detect this in internet explorer by verifying that the mouse is
18128          * down during the mousemove event.  Firefox doesn't give us the
18129          * button state on the mousemove event.
18130          * @method handleMouseMove
18131          * @param {Event} e the event
18132          * @private
18133          * @static
18134          */
18135         handleMouseMove: function(e) {
18136             if (! this.dragCurrent) {
18137                 return true;
18138             }
18139
18140             // var button = e.which || e.button;
18141
18142             // check for IE mouseup outside of page boundary
18143             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18144                 this.stopEvent(e);
18145                 return this.handleMouseUp(e);
18146             }
18147
18148             if (!this.dragThreshMet) {
18149                 var diffX = Math.abs(this.startX - e.getPageX());
18150                 var diffY = Math.abs(this.startY - e.getPageY());
18151                 if (diffX > this.clickPixelThresh ||
18152                             diffY > this.clickPixelThresh) {
18153                     this.startDrag(this.startX, this.startY);
18154                 }
18155             }
18156
18157             if (this.dragThreshMet) {
18158                 this.dragCurrent.b4Drag(e);
18159                 this.dragCurrent.onDrag(e);
18160                 if(!this.dragCurrent.moveOnly){
18161                     this.fireEvents(e, false);
18162                 }
18163             }
18164
18165             this.stopEvent(e);
18166
18167             return true;
18168         },
18169
18170         /**
18171          * Iterates over all of the DragDrop elements to find ones we are
18172          * hovering over or dropping on
18173          * @method fireEvents
18174          * @param {Event} e the event
18175          * @param {boolean} isDrop is this a drop op or a mouseover op?
18176          * @private
18177          * @static
18178          */
18179         fireEvents: function(e, isDrop) {
18180             var dc = this.dragCurrent;
18181
18182             // If the user did the mouse up outside of the window, we could
18183             // get here even though we have ended the drag.
18184             if (!dc || dc.isLocked()) {
18185                 return;
18186             }
18187
18188             var pt = e.getPoint();
18189
18190             // cache the previous dragOver array
18191             var oldOvers = [];
18192
18193             var outEvts   = [];
18194             var overEvts  = [];
18195             var dropEvts  = [];
18196             var enterEvts = [];
18197
18198             // Check to see if the object(s) we were hovering over is no longer
18199             // being hovered over so we can fire the onDragOut event
18200             for (var i in this.dragOvers) {
18201
18202                 var ddo = this.dragOvers[i];
18203
18204                 if (! this.isTypeOfDD(ddo)) {
18205                     continue;
18206                 }
18207
18208                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18209                     outEvts.push( ddo );
18210                 }
18211
18212                 oldOvers[i] = true;
18213                 delete this.dragOvers[i];
18214             }
18215
18216             for (var sGroup in dc.groups) {
18217
18218                 if ("string" != typeof sGroup) {
18219                     continue;
18220                 }
18221
18222                 for (i in this.ids[sGroup]) {
18223                     var oDD = this.ids[sGroup][i];
18224                     if (! this.isTypeOfDD(oDD)) {
18225                         continue;
18226                     }
18227
18228                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18229                         if (this.isOverTarget(pt, oDD, this.mode)) {
18230                             // look for drop interactions
18231                             if (isDrop) {
18232                                 dropEvts.push( oDD );
18233                             // look for drag enter and drag over interactions
18234                             } else {
18235
18236                                 // initial drag over: dragEnter fires
18237                                 if (!oldOvers[oDD.id]) {
18238                                     enterEvts.push( oDD );
18239                                 // subsequent drag overs: dragOver fires
18240                                 } else {
18241                                     overEvts.push( oDD );
18242                                 }
18243
18244                                 this.dragOvers[oDD.id] = oDD;
18245                             }
18246                         }
18247                     }
18248                 }
18249             }
18250
18251             if (this.mode) {
18252                 if (outEvts.length) {
18253                     dc.b4DragOut(e, outEvts);
18254                     dc.onDragOut(e, outEvts);
18255                 }
18256
18257                 if (enterEvts.length) {
18258                     dc.onDragEnter(e, enterEvts);
18259                 }
18260
18261                 if (overEvts.length) {
18262                     dc.b4DragOver(e, overEvts);
18263                     dc.onDragOver(e, overEvts);
18264                 }
18265
18266                 if (dropEvts.length) {
18267                     dc.b4DragDrop(e, dropEvts);
18268                     dc.onDragDrop(e, dropEvts);
18269                 }
18270
18271             } else {
18272                 // fire dragout events
18273                 var len = 0;
18274                 for (i=0, len=outEvts.length; i<len; ++i) {
18275                     dc.b4DragOut(e, outEvts[i].id);
18276                     dc.onDragOut(e, outEvts[i].id);
18277                 }
18278
18279                 // fire enter events
18280                 for (i=0,len=enterEvts.length; i<len; ++i) {
18281                     // dc.b4DragEnter(e, oDD.id);
18282                     dc.onDragEnter(e, enterEvts[i].id);
18283                 }
18284
18285                 // fire over events
18286                 for (i=0,len=overEvts.length; i<len; ++i) {
18287                     dc.b4DragOver(e, overEvts[i].id);
18288                     dc.onDragOver(e, overEvts[i].id);
18289                 }
18290
18291                 // fire drop events
18292                 for (i=0, len=dropEvts.length; i<len; ++i) {
18293                     dc.b4DragDrop(e, dropEvts[i].id);
18294                     dc.onDragDrop(e, dropEvts[i].id);
18295                 }
18296
18297             }
18298
18299             // notify about a drop that did not find a target
18300             if (isDrop && !dropEvts.length) {
18301                 dc.onInvalidDrop(e);
18302             }
18303
18304         },
18305
18306         /**
18307          * Helper function for getting the best match from the list of drag
18308          * and drop objects returned by the drag and drop events when we are
18309          * in INTERSECT mode.  It returns either the first object that the
18310          * cursor is over, or the object that has the greatest overlap with
18311          * the dragged element.
18312          * @method getBestMatch
18313          * @param  {DragDrop[]} dds The array of drag and drop objects
18314          * targeted
18315          * @return {DragDrop}       The best single match
18316          * @static
18317          */
18318         getBestMatch: function(dds) {
18319             var winner = null;
18320             // Return null if the input is not what we expect
18321             //if (!dds || !dds.length || dds.length == 0) {
18322                // winner = null;
18323             // If there is only one item, it wins
18324             //} else if (dds.length == 1) {
18325
18326             var len = dds.length;
18327
18328             if (len == 1) {
18329                 winner = dds[0];
18330             } else {
18331                 // Loop through the targeted items
18332                 for (var i=0; i<len; ++i) {
18333                     var dd = dds[i];
18334                     // If the cursor is over the object, it wins.  If the
18335                     // cursor is over multiple matches, the first one we come
18336                     // to wins.
18337                     if (dd.cursorIsOver) {
18338                         winner = dd;
18339                         break;
18340                     // Otherwise the object with the most overlap wins
18341                     } else {
18342                         if (!winner ||
18343                             winner.overlap.getArea() < dd.overlap.getArea()) {
18344                             winner = dd;
18345                         }
18346                     }
18347                 }
18348             }
18349
18350             return winner;
18351         },
18352
18353         /**
18354          * Refreshes the cache of the top-left and bottom-right points of the
18355          * drag and drop objects in the specified group(s).  This is in the
18356          * format that is stored in the drag and drop instance, so typical
18357          * usage is:
18358          * <code>
18359          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18360          * </code>
18361          * Alternatively:
18362          * <code>
18363          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18364          * </code>
18365          * @TODO this really should be an indexed array.  Alternatively this
18366          * method could accept both.
18367          * @method refreshCache
18368          * @param {Object} groups an associative array of groups to refresh
18369          * @static
18370          */
18371         refreshCache: function(groups) {
18372             for (var sGroup in groups) {
18373                 if ("string" != typeof sGroup) {
18374                     continue;
18375                 }
18376                 for (var i in this.ids[sGroup]) {
18377                     var oDD = this.ids[sGroup][i];
18378
18379                     if (this.isTypeOfDD(oDD)) {
18380                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18381                         var loc = this.getLocation(oDD);
18382                         if (loc) {
18383                             this.locationCache[oDD.id] = loc;
18384                         } else {
18385                             delete this.locationCache[oDD.id];
18386                             // this will unregister the drag and drop object if
18387                             // the element is not in a usable state
18388                             // oDD.unreg();
18389                         }
18390                     }
18391                 }
18392             }
18393         },
18394
18395         /**
18396          * This checks to make sure an element exists and is in the DOM.  The
18397          * main purpose is to handle cases where innerHTML is used to remove
18398          * drag and drop objects from the DOM.  IE provides an 'unspecified
18399          * error' when trying to access the offsetParent of such an element
18400          * @method verifyEl
18401          * @param {HTMLElement} el the element to check
18402          * @return {boolean} true if the element looks usable
18403          * @static
18404          */
18405         verifyEl: function(el) {
18406             if (el) {
18407                 var parent;
18408                 if(Roo.isIE){
18409                     try{
18410                         parent = el.offsetParent;
18411                     }catch(e){}
18412                 }else{
18413                     parent = el.offsetParent;
18414                 }
18415                 if (parent) {
18416                     return true;
18417                 }
18418             }
18419
18420             return false;
18421         },
18422
18423         /**
18424          * Returns a Region object containing the drag and drop element's position
18425          * and size, including the padding configured for it
18426          * @method getLocation
18427          * @param {DragDrop} oDD the drag and drop object to get the
18428          *                       location for
18429          * @return {Roo.lib.Region} a Region object representing the total area
18430          *                             the element occupies, including any padding
18431          *                             the instance is configured for.
18432          * @static
18433          */
18434         getLocation: function(oDD) {
18435             if (! this.isTypeOfDD(oDD)) {
18436                 return null;
18437             }
18438
18439             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18440
18441             try {
18442                 pos= Roo.lib.Dom.getXY(el);
18443             } catch (e) { }
18444
18445             if (!pos) {
18446                 return null;
18447             }
18448
18449             x1 = pos[0];
18450             x2 = x1 + el.offsetWidth;
18451             y1 = pos[1];
18452             y2 = y1 + el.offsetHeight;
18453
18454             t = y1 - oDD.padding[0];
18455             r = x2 + oDD.padding[1];
18456             b = y2 + oDD.padding[2];
18457             l = x1 - oDD.padding[3];
18458
18459             return new Roo.lib.Region( t, r, b, l );
18460         },
18461
18462         /**
18463          * Checks the cursor location to see if it over the target
18464          * @method isOverTarget
18465          * @param {Roo.lib.Point} pt The point to evaluate
18466          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18467          * @return {boolean} true if the mouse is over the target
18468          * @private
18469          * @static
18470          */
18471         isOverTarget: function(pt, oTarget, intersect) {
18472             // use cache if available
18473             var loc = this.locationCache[oTarget.id];
18474             if (!loc || !this.useCache) {
18475                 loc = this.getLocation(oTarget);
18476                 this.locationCache[oTarget.id] = loc;
18477
18478             }
18479
18480             if (!loc) {
18481                 return false;
18482             }
18483
18484             oTarget.cursorIsOver = loc.contains( pt );
18485
18486             // DragDrop is using this as a sanity check for the initial mousedown
18487             // in this case we are done.  In POINT mode, if the drag obj has no
18488             // contraints, we are also done. Otherwise we need to evaluate the
18489             // location of the target as related to the actual location of the
18490             // dragged element.
18491             var dc = this.dragCurrent;
18492             if (!dc || !dc.getTargetCoord ||
18493                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18494                 return oTarget.cursorIsOver;
18495             }
18496
18497             oTarget.overlap = null;
18498
18499             // Get the current location of the drag element, this is the
18500             // location of the mouse event less the delta that represents
18501             // where the original mousedown happened on the element.  We
18502             // need to consider constraints and ticks as well.
18503             var pos = dc.getTargetCoord(pt.x, pt.y);
18504
18505             var el = dc.getDragEl();
18506             var curRegion = new Roo.lib.Region( pos.y,
18507                                                    pos.x + el.offsetWidth,
18508                                                    pos.y + el.offsetHeight,
18509                                                    pos.x );
18510
18511             var overlap = curRegion.intersect(loc);
18512
18513             if (overlap) {
18514                 oTarget.overlap = overlap;
18515                 return (intersect) ? true : oTarget.cursorIsOver;
18516             } else {
18517                 return false;
18518             }
18519         },
18520
18521         /**
18522          * unload event handler
18523          * @method _onUnload
18524          * @private
18525          * @static
18526          */
18527         _onUnload: function(e, me) {
18528             Roo.dd.DragDropMgr.unregAll();
18529         },
18530
18531         /**
18532          * Cleans up the drag and drop events and objects.
18533          * @method unregAll
18534          * @private
18535          * @static
18536          */
18537         unregAll: function() {
18538
18539             if (this.dragCurrent) {
18540                 this.stopDrag();
18541                 this.dragCurrent = null;
18542             }
18543
18544             this._execOnAll("unreg", []);
18545
18546             for (i in this.elementCache) {
18547                 delete this.elementCache[i];
18548             }
18549
18550             this.elementCache = {};
18551             this.ids = {};
18552         },
18553
18554         /**
18555          * A cache of DOM elements
18556          * @property elementCache
18557          * @private
18558          * @static
18559          */
18560         elementCache: {},
18561
18562         /**
18563          * Get the wrapper for the DOM element specified
18564          * @method getElWrapper
18565          * @param {String} id the id of the element to get
18566          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18567          * @private
18568          * @deprecated This wrapper isn't that useful
18569          * @static
18570          */
18571         getElWrapper: function(id) {
18572             var oWrapper = this.elementCache[id];
18573             if (!oWrapper || !oWrapper.el) {
18574                 oWrapper = this.elementCache[id] =
18575                     new this.ElementWrapper(Roo.getDom(id));
18576             }
18577             return oWrapper;
18578         },
18579
18580         /**
18581          * Returns the actual DOM element
18582          * @method getElement
18583          * @param {String} id the id of the elment to get
18584          * @return {Object} The element
18585          * @deprecated use Roo.getDom instead
18586          * @static
18587          */
18588         getElement: function(id) {
18589             return Roo.getDom(id);
18590         },
18591
18592         /**
18593          * Returns the style property for the DOM element (i.e.,
18594          * document.getElById(id).style)
18595          * @method getCss
18596          * @param {String} id the id of the elment to get
18597          * @return {Object} The style property of the element
18598          * @deprecated use Roo.getDom instead
18599          * @static
18600          */
18601         getCss: function(id) {
18602             var el = Roo.getDom(id);
18603             return (el) ? el.style : null;
18604         },
18605
18606         /**
18607          * Inner class for cached elements
18608          * @class DragDropMgr.ElementWrapper
18609          * @for DragDropMgr
18610          * @private
18611          * @deprecated
18612          */
18613         ElementWrapper: function(el) {
18614                 /**
18615                  * The element
18616                  * @property el
18617                  */
18618                 this.el = el || null;
18619                 /**
18620                  * The element id
18621                  * @property id
18622                  */
18623                 this.id = this.el && el.id;
18624                 /**
18625                  * A reference to the style property
18626                  * @property css
18627                  */
18628                 this.css = this.el && el.style;
18629             },
18630
18631         /**
18632          * Returns the X position of an html element
18633          * @method getPosX
18634          * @param el the element for which to get the position
18635          * @return {int} the X coordinate
18636          * @for DragDropMgr
18637          * @deprecated use Roo.lib.Dom.getX instead
18638          * @static
18639          */
18640         getPosX: function(el) {
18641             return Roo.lib.Dom.getX(el);
18642         },
18643
18644         /**
18645          * Returns the Y position of an html element
18646          * @method getPosY
18647          * @param el the element for which to get the position
18648          * @return {int} the Y coordinate
18649          * @deprecated use Roo.lib.Dom.getY instead
18650          * @static
18651          */
18652         getPosY: function(el) {
18653             return Roo.lib.Dom.getY(el);
18654         },
18655
18656         /**
18657          * Swap two nodes.  In IE, we use the native method, for others we
18658          * emulate the IE behavior
18659          * @method swapNode
18660          * @param n1 the first node to swap
18661          * @param n2 the other node to swap
18662          * @static
18663          */
18664         swapNode: function(n1, n2) {
18665             if (n1.swapNode) {
18666                 n1.swapNode(n2);
18667             } else {
18668                 var p = n2.parentNode;
18669                 var s = n2.nextSibling;
18670
18671                 if (s == n1) {
18672                     p.insertBefore(n1, n2);
18673                 } else if (n2 == n1.nextSibling) {
18674                     p.insertBefore(n2, n1);
18675                 } else {
18676                     n1.parentNode.replaceChild(n2, n1);
18677                     p.insertBefore(n1, s);
18678                 }
18679             }
18680         },
18681
18682         /**
18683          * Returns the current scroll position
18684          * @method getScroll
18685          * @private
18686          * @static
18687          */
18688         getScroll: function () {
18689             var t, l, dde=document.documentElement, db=document.body;
18690             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18691                 t = dde.scrollTop;
18692                 l = dde.scrollLeft;
18693             } else if (db) {
18694                 t = db.scrollTop;
18695                 l = db.scrollLeft;
18696             } else {
18697
18698             }
18699             return { top: t, left: l };
18700         },
18701
18702         /**
18703          * Returns the specified element style property
18704          * @method getStyle
18705          * @param {HTMLElement} el          the element
18706          * @param {string}      styleProp   the style property
18707          * @return {string} The value of the style property
18708          * @deprecated use Roo.lib.Dom.getStyle
18709          * @static
18710          */
18711         getStyle: function(el, styleProp) {
18712             return Roo.fly(el).getStyle(styleProp);
18713         },
18714
18715         /**
18716          * Gets the scrollTop
18717          * @method getScrollTop
18718          * @return {int} the document's scrollTop
18719          * @static
18720          */
18721         getScrollTop: function () { return this.getScroll().top; },
18722
18723         /**
18724          * Gets the scrollLeft
18725          * @method getScrollLeft
18726          * @return {int} the document's scrollTop
18727          * @static
18728          */
18729         getScrollLeft: function () { return this.getScroll().left; },
18730
18731         /**
18732          * Sets the x/y position of an element to the location of the
18733          * target element.
18734          * @method moveToEl
18735          * @param {HTMLElement} moveEl      The element to move
18736          * @param {HTMLElement} targetEl    The position reference element
18737          * @static
18738          */
18739         moveToEl: function (moveEl, targetEl) {
18740             var aCoord = Roo.lib.Dom.getXY(targetEl);
18741             Roo.lib.Dom.setXY(moveEl, aCoord);
18742         },
18743
18744         /**
18745          * Numeric array sort function
18746          * @method numericSort
18747          * @static
18748          */
18749         numericSort: function(a, b) { return (a - b); },
18750
18751         /**
18752          * Internal counter
18753          * @property _timeoutCount
18754          * @private
18755          * @static
18756          */
18757         _timeoutCount: 0,
18758
18759         /**
18760          * Trying to make the load order less important.  Without this we get
18761          * an error if this file is loaded before the Event Utility.
18762          * @method _addListeners
18763          * @private
18764          * @static
18765          */
18766         _addListeners: function() {
18767             var DDM = Roo.dd.DDM;
18768             if ( Roo.lib.Event && document ) {
18769                 DDM._onLoad();
18770             } else {
18771                 if (DDM._timeoutCount > 2000) {
18772                 } else {
18773                     setTimeout(DDM._addListeners, 10);
18774                     if (document && document.body) {
18775                         DDM._timeoutCount += 1;
18776                     }
18777                 }
18778             }
18779         },
18780
18781         /**
18782          * Recursively searches the immediate parent and all child nodes for
18783          * the handle element in order to determine wheter or not it was
18784          * clicked.
18785          * @method handleWasClicked
18786          * @param node the html element to inspect
18787          * @static
18788          */
18789         handleWasClicked: function(node, id) {
18790             if (this.isHandle(id, node.id)) {
18791                 return true;
18792             } else {
18793                 // check to see if this is a text node child of the one we want
18794                 var p = node.parentNode;
18795
18796                 while (p) {
18797                     if (this.isHandle(id, p.id)) {
18798                         return true;
18799                     } else {
18800                         p = p.parentNode;
18801                     }
18802                 }
18803             }
18804
18805             return false;
18806         }
18807
18808     };
18809
18810 }();
18811
18812 // shorter alias, save a few bytes
18813 Roo.dd.DDM = Roo.dd.DragDropMgr;
18814 Roo.dd.DDM._addListeners();
18815
18816 }/*
18817  * Based on:
18818  * Ext JS Library 1.1.1
18819  * Copyright(c) 2006-2007, Ext JS, LLC.
18820  *
18821  * Originally Released Under LGPL - original licence link has changed is not relivant.
18822  *
18823  * Fork - LGPL
18824  * <script type="text/javascript">
18825  */
18826
18827 /**
18828  * @class Roo.dd.DD
18829  * A DragDrop implementation where the linked element follows the
18830  * mouse cursor during a drag.
18831  * @extends Roo.dd.DragDrop
18832  * @constructor
18833  * @param {String} id the id of the linked element
18834  * @param {String} sGroup the group of related DragDrop items
18835  * @param {object} config an object containing configurable attributes
18836  *                Valid properties for DD:
18837  *                    scroll
18838  */
18839 Roo.dd.DD = function(id, sGroup, config) {
18840     if (id) {
18841         this.init(id, sGroup, config);
18842     }
18843 };
18844
18845 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18846
18847     /**
18848      * When set to true, the utility automatically tries to scroll the browser
18849      * window wehn a drag and drop element is dragged near the viewport boundary.
18850      * Defaults to true.
18851      * @property scroll
18852      * @type boolean
18853      */
18854     scroll: true,
18855
18856     /**
18857      * Sets the pointer offset to the distance between the linked element's top
18858      * left corner and the location the element was clicked
18859      * @method autoOffset
18860      * @param {int} iPageX the X coordinate of the click
18861      * @param {int} iPageY the Y coordinate of the click
18862      */
18863     autoOffset: function(iPageX, iPageY) {
18864         var x = iPageX - this.startPageX;
18865         var y = iPageY - this.startPageY;
18866         this.setDelta(x, y);
18867     },
18868
18869     /**
18870      * Sets the pointer offset.  You can call this directly to force the
18871      * offset to be in a particular location (e.g., pass in 0,0 to set it
18872      * to the center of the object)
18873      * @method setDelta
18874      * @param {int} iDeltaX the distance from the left
18875      * @param {int} iDeltaY the distance from the top
18876      */
18877     setDelta: function(iDeltaX, iDeltaY) {
18878         this.deltaX = iDeltaX;
18879         this.deltaY = iDeltaY;
18880     },
18881
18882     /**
18883      * Sets the drag element to the location of the mousedown or click event,
18884      * maintaining the cursor location relative to the location on the element
18885      * that was clicked.  Override this if you want to place the element in a
18886      * location other than where the cursor is.
18887      * @method setDragElPos
18888      * @param {int} iPageX the X coordinate of the mousedown or drag event
18889      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18890      */
18891     setDragElPos: function(iPageX, iPageY) {
18892         // the first time we do this, we are going to check to make sure
18893         // the element has css positioning
18894
18895         var el = this.getDragEl();
18896         this.alignElWithMouse(el, iPageX, iPageY);
18897     },
18898
18899     /**
18900      * Sets the element to the location of the mousedown or click event,
18901      * maintaining the cursor location relative to the location on the element
18902      * that was clicked.  Override this if you want to place the element in a
18903      * location other than where the cursor is.
18904      * @method alignElWithMouse
18905      * @param {HTMLElement} el the element to move
18906      * @param {int} iPageX the X coordinate of the mousedown or drag event
18907      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18908      */
18909     alignElWithMouse: function(el, iPageX, iPageY) {
18910         var oCoord = this.getTargetCoord(iPageX, iPageY);
18911         var fly = el.dom ? el : Roo.fly(el);
18912         if (!this.deltaSetXY) {
18913             var aCoord = [oCoord.x, oCoord.y];
18914             fly.setXY(aCoord);
18915             var newLeft = fly.getLeft(true);
18916             var newTop  = fly.getTop(true);
18917             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18918         } else {
18919             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18920         }
18921
18922         this.cachePosition(oCoord.x, oCoord.y);
18923         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18924         return oCoord;
18925     },
18926
18927     /**
18928      * Saves the most recent position so that we can reset the constraints and
18929      * tick marks on-demand.  We need to know this so that we can calculate the
18930      * number of pixels the element is offset from its original position.
18931      * @method cachePosition
18932      * @param iPageX the current x position (optional, this just makes it so we
18933      * don't have to look it up again)
18934      * @param iPageY the current y position (optional, this just makes it so we
18935      * don't have to look it up again)
18936      */
18937     cachePosition: function(iPageX, iPageY) {
18938         if (iPageX) {
18939             this.lastPageX = iPageX;
18940             this.lastPageY = iPageY;
18941         } else {
18942             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18943             this.lastPageX = aCoord[0];
18944             this.lastPageY = aCoord[1];
18945         }
18946     },
18947
18948     /**
18949      * Auto-scroll the window if the dragged object has been moved beyond the
18950      * visible window boundary.
18951      * @method autoScroll
18952      * @param {int} x the drag element's x position
18953      * @param {int} y the drag element's y position
18954      * @param {int} h the height of the drag element
18955      * @param {int} w the width of the drag element
18956      * @private
18957      */
18958     autoScroll: function(x, y, h, w) {
18959
18960         if (this.scroll) {
18961             // The client height
18962             var clientH = Roo.lib.Dom.getViewWidth();
18963
18964             // The client width
18965             var clientW = Roo.lib.Dom.getViewHeight();
18966
18967             // The amt scrolled down
18968             var st = this.DDM.getScrollTop();
18969
18970             // The amt scrolled right
18971             var sl = this.DDM.getScrollLeft();
18972
18973             // Location of the bottom of the element
18974             var bot = h + y;
18975
18976             // Location of the right of the element
18977             var right = w + x;
18978
18979             // The distance from the cursor to the bottom of the visible area,
18980             // adjusted so that we don't scroll if the cursor is beyond the
18981             // element drag constraints
18982             var toBot = (clientH + st - y - this.deltaY);
18983
18984             // The distance from the cursor to the right of the visible area
18985             var toRight = (clientW + sl - x - this.deltaX);
18986
18987
18988             // How close to the edge the cursor must be before we scroll
18989             // var thresh = (document.all) ? 100 : 40;
18990             var thresh = 40;
18991
18992             // How many pixels to scroll per autoscroll op.  This helps to reduce
18993             // clunky scrolling. IE is more sensitive about this ... it needs this
18994             // value to be higher.
18995             var scrAmt = (document.all) ? 80 : 30;
18996
18997             // Scroll down if we are near the bottom of the visible page and the
18998             // obj extends below the crease
18999             if ( bot > clientH && toBot < thresh ) {
19000                 window.scrollTo(sl, st + scrAmt);
19001             }
19002
19003             // Scroll up if the window is scrolled down and the top of the object
19004             // goes above the top border
19005             if ( y < st && st > 0 && y - st < thresh ) {
19006                 window.scrollTo(sl, st - scrAmt);
19007             }
19008
19009             // Scroll right if the obj is beyond the right border and the cursor is
19010             // near the border.
19011             if ( right > clientW && toRight < thresh ) {
19012                 window.scrollTo(sl + scrAmt, st);
19013             }
19014
19015             // Scroll left if the window has been scrolled to the right and the obj
19016             // extends past the left border
19017             if ( x < sl && sl > 0 && x - sl < thresh ) {
19018                 window.scrollTo(sl - scrAmt, st);
19019             }
19020         }
19021     },
19022
19023     /**
19024      * Finds the location the element should be placed if we want to move
19025      * it to where the mouse location less the click offset would place us.
19026      * @method getTargetCoord
19027      * @param {int} iPageX the X coordinate of the click
19028      * @param {int} iPageY the Y coordinate of the click
19029      * @return an object that contains the coordinates (Object.x and Object.y)
19030      * @private
19031      */
19032     getTargetCoord: function(iPageX, iPageY) {
19033
19034
19035         var x = iPageX - this.deltaX;
19036         var y = iPageY - this.deltaY;
19037
19038         if (this.constrainX) {
19039             if (x < this.minX) { x = this.minX; }
19040             if (x > this.maxX) { x = this.maxX; }
19041         }
19042
19043         if (this.constrainY) {
19044             if (y < this.minY) { y = this.minY; }
19045             if (y > this.maxY) { y = this.maxY; }
19046         }
19047
19048         x = this.getTick(x, this.xTicks);
19049         y = this.getTick(y, this.yTicks);
19050
19051
19052         return {x:x, y:y};
19053     },
19054
19055     /*
19056      * Sets up config options specific to this class. Overrides
19057      * Roo.dd.DragDrop, but all versions of this method through the
19058      * inheritance chain are called
19059      */
19060     applyConfig: function() {
19061         Roo.dd.DD.superclass.applyConfig.call(this);
19062         this.scroll = (this.config.scroll !== false);
19063     },
19064
19065     /*
19066      * Event that fires prior to the onMouseDown event.  Overrides
19067      * Roo.dd.DragDrop.
19068      */
19069     b4MouseDown: function(e) {
19070         // this.resetConstraints();
19071         this.autoOffset(e.getPageX(),
19072                             e.getPageY());
19073     },
19074
19075     /*
19076      * Event that fires prior to the onDrag event.  Overrides
19077      * Roo.dd.DragDrop.
19078      */
19079     b4Drag: function(e) {
19080         this.setDragElPos(e.getPageX(),
19081                             e.getPageY());
19082     },
19083
19084     toString: function() {
19085         return ("DD " + this.id);
19086     }
19087
19088     //////////////////////////////////////////////////////////////////////////
19089     // Debugging ygDragDrop events that can be overridden
19090     //////////////////////////////////////////////////////////////////////////
19091     /*
19092     startDrag: function(x, y) {
19093     },
19094
19095     onDrag: function(e) {
19096     },
19097
19098     onDragEnter: function(e, id) {
19099     },
19100
19101     onDragOver: function(e, id) {
19102     },
19103
19104     onDragOut: function(e, id) {
19105     },
19106
19107     onDragDrop: function(e, id) {
19108     },
19109
19110     endDrag: function(e) {
19111     }
19112
19113     */
19114
19115 });/*
19116  * Based on:
19117  * Ext JS Library 1.1.1
19118  * Copyright(c) 2006-2007, Ext JS, LLC.
19119  *
19120  * Originally Released Under LGPL - original licence link has changed is not relivant.
19121  *
19122  * Fork - LGPL
19123  * <script type="text/javascript">
19124  */
19125
19126 /**
19127  * @class Roo.dd.DDProxy
19128  * A DragDrop implementation that inserts an empty, bordered div into
19129  * the document that follows the cursor during drag operations.  At the time of
19130  * the click, the frame div is resized to the dimensions of the linked html
19131  * element, and moved to the exact location of the linked element.
19132  *
19133  * References to the "frame" element refer to the single proxy element that
19134  * was created to be dragged in place of all DDProxy elements on the
19135  * page.
19136  *
19137  * @extends Roo.dd.DD
19138  * @constructor
19139  * @param {String} id the id of the linked html element
19140  * @param {String} sGroup the group of related DragDrop objects
19141  * @param {object} config an object containing configurable attributes
19142  *                Valid properties for DDProxy in addition to those in DragDrop:
19143  *                   resizeFrame, centerFrame, dragElId
19144  */
19145 Roo.dd.DDProxy = function(id, sGroup, config) {
19146     if (id) {
19147         this.init(id, sGroup, config);
19148         this.initFrame();
19149     }
19150 };
19151
19152 /**
19153  * The default drag frame div id
19154  * @property Roo.dd.DDProxy.dragElId
19155  * @type String
19156  * @static
19157  */
19158 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19159
19160 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19161
19162     /**
19163      * By default we resize the drag frame to be the same size as the element
19164      * we want to drag (this is to get the frame effect).  We can turn it off
19165      * if we want a different behavior.
19166      * @property resizeFrame
19167      * @type boolean
19168      */
19169     resizeFrame: true,
19170
19171     /**
19172      * By default the frame is positioned exactly where the drag element is, so
19173      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19174      * you do not have constraints on the obj is to have the drag frame centered
19175      * around the cursor.  Set centerFrame to true for this effect.
19176      * @property centerFrame
19177      * @type boolean
19178      */
19179     centerFrame: false,
19180
19181     /**
19182      * Creates the proxy element if it does not yet exist
19183      * @method createFrame
19184      */
19185     createFrame: function() {
19186         var self = this;
19187         var body = document.body;
19188
19189         if (!body || !body.firstChild) {
19190             setTimeout( function() { self.createFrame(); }, 50 );
19191             return;
19192         }
19193
19194         var div = this.getDragEl();
19195
19196         if (!div) {
19197             div    = document.createElement("div");
19198             div.id = this.dragElId;
19199             var s  = div.style;
19200
19201             s.position   = "absolute";
19202             s.visibility = "hidden";
19203             s.cursor     = "move";
19204             s.border     = "2px solid #aaa";
19205             s.zIndex     = 999;
19206
19207             // appendChild can blow up IE if invoked prior to the window load event
19208             // while rendering a table.  It is possible there are other scenarios
19209             // that would cause this to happen as well.
19210             body.insertBefore(div, body.firstChild);
19211         }
19212     },
19213
19214     /**
19215      * Initialization for the drag frame element.  Must be called in the
19216      * constructor of all subclasses
19217      * @method initFrame
19218      */
19219     initFrame: function() {
19220         this.createFrame();
19221     },
19222
19223     applyConfig: function() {
19224         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19225
19226         this.resizeFrame = (this.config.resizeFrame !== false);
19227         this.centerFrame = (this.config.centerFrame);
19228         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19229     },
19230
19231     /**
19232      * Resizes the drag frame to the dimensions of the clicked object, positions
19233      * it over the object, and finally displays it
19234      * @method showFrame
19235      * @param {int} iPageX X click position
19236      * @param {int} iPageY Y click position
19237      * @private
19238      */
19239     showFrame: function(iPageX, iPageY) {
19240         var el = this.getEl();
19241         var dragEl = this.getDragEl();
19242         var s = dragEl.style;
19243
19244         this._resizeProxy();
19245
19246         if (this.centerFrame) {
19247             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19248                            Math.round(parseInt(s.height, 10)/2) );
19249         }
19250
19251         this.setDragElPos(iPageX, iPageY);
19252
19253         Roo.fly(dragEl).show();
19254     },
19255
19256     /**
19257      * The proxy is automatically resized to the dimensions of the linked
19258      * element when a drag is initiated, unless resizeFrame is set to false
19259      * @method _resizeProxy
19260      * @private
19261      */
19262     _resizeProxy: function() {
19263         if (this.resizeFrame) {
19264             var el = this.getEl();
19265             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19266         }
19267     },
19268
19269     // overrides Roo.dd.DragDrop
19270     b4MouseDown: function(e) {
19271         var x = e.getPageX();
19272         var y = e.getPageY();
19273         this.autoOffset(x, y);
19274         this.setDragElPos(x, y);
19275     },
19276
19277     // overrides Roo.dd.DragDrop
19278     b4StartDrag: function(x, y) {
19279         // show the drag frame
19280         this.showFrame(x, y);
19281     },
19282
19283     // overrides Roo.dd.DragDrop
19284     b4EndDrag: function(e) {
19285         Roo.fly(this.getDragEl()).hide();
19286     },
19287
19288     // overrides Roo.dd.DragDrop
19289     // By default we try to move the element to the last location of the frame.
19290     // This is so that the default behavior mirrors that of Roo.dd.DD.
19291     endDrag: function(e) {
19292
19293         var lel = this.getEl();
19294         var del = this.getDragEl();
19295
19296         // Show the drag frame briefly so we can get its position
19297         del.style.visibility = "";
19298
19299         this.beforeMove();
19300         // Hide the linked element before the move to get around a Safari
19301         // rendering bug.
19302         lel.style.visibility = "hidden";
19303         Roo.dd.DDM.moveToEl(lel, del);
19304         del.style.visibility = "hidden";
19305         lel.style.visibility = "";
19306
19307         this.afterDrag();
19308     },
19309
19310     beforeMove : function(){
19311
19312     },
19313
19314     afterDrag : function(){
19315
19316     },
19317
19318     toString: function() {
19319         return ("DDProxy " + this.id);
19320     }
19321
19322 });
19323 /*
19324  * Based on:
19325  * Ext JS Library 1.1.1
19326  * Copyright(c) 2006-2007, Ext JS, LLC.
19327  *
19328  * Originally Released Under LGPL - original licence link has changed is not relivant.
19329  *
19330  * Fork - LGPL
19331  * <script type="text/javascript">
19332  */
19333
19334  /**
19335  * @class Roo.dd.DDTarget
19336  * A DragDrop implementation that does not move, but can be a drop
19337  * target.  You would get the same result by simply omitting implementation
19338  * for the event callbacks, but this way we reduce the processing cost of the
19339  * event listener and the callbacks.
19340  * @extends Roo.dd.DragDrop
19341  * @constructor
19342  * @param {String} id the id of the element that is a drop target
19343  * @param {String} sGroup the group of related DragDrop objects
19344  * @param {object} config an object containing configurable attributes
19345  *                 Valid properties for DDTarget in addition to those in
19346  *                 DragDrop:
19347  *                    none
19348  */
19349 Roo.dd.DDTarget = function(id, sGroup, config) {
19350     if (id) {
19351         this.initTarget(id, sGroup, config);
19352     }
19353     if (config.listeners || config.events) { 
19354        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19355             listeners : config.listeners || {}, 
19356             events : config.events || {} 
19357         });    
19358     }
19359 };
19360
19361 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19362 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19363     toString: function() {
19364         return ("DDTarget " + this.id);
19365     }
19366 });
19367 /*
19368  * Based on:
19369  * Ext JS Library 1.1.1
19370  * Copyright(c) 2006-2007, Ext JS, LLC.
19371  *
19372  * Originally Released Under LGPL - original licence link has changed is not relivant.
19373  *
19374  * Fork - LGPL
19375  * <script type="text/javascript">
19376  */
19377  
19378
19379 /**
19380  * @class Roo.dd.ScrollManager
19381  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19382  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19383  * @singleton
19384  */
19385 Roo.dd.ScrollManager = function(){
19386     var ddm = Roo.dd.DragDropMgr;
19387     var els = {};
19388     var dragEl = null;
19389     var proc = {};
19390     
19391     
19392     
19393     var onStop = function(e){
19394         dragEl = null;
19395         clearProc();
19396     };
19397     
19398     var triggerRefresh = function(){
19399         if(ddm.dragCurrent){
19400              ddm.refreshCache(ddm.dragCurrent.groups);
19401         }
19402     };
19403     
19404     var doScroll = function(){
19405         if(ddm.dragCurrent){
19406             var dds = Roo.dd.ScrollManager;
19407             if(!dds.animate){
19408                 if(proc.el.scroll(proc.dir, dds.increment)){
19409                     triggerRefresh();
19410                 }
19411             }else{
19412                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19413             }
19414         }
19415     };
19416     
19417     var clearProc = function(){
19418         if(proc.id){
19419             clearInterval(proc.id);
19420         }
19421         proc.id = 0;
19422         proc.el = null;
19423         proc.dir = "";
19424     };
19425     
19426     var startProc = function(el, dir){
19427          Roo.log('scroll startproc');
19428         clearProc();
19429         proc.el = el;
19430         proc.dir = dir;
19431         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19432     };
19433     
19434     var onFire = function(e, isDrop){
19435        
19436         if(isDrop || !ddm.dragCurrent){ return; }
19437         var dds = Roo.dd.ScrollManager;
19438         if(!dragEl || dragEl != ddm.dragCurrent){
19439             dragEl = ddm.dragCurrent;
19440             // refresh regions on drag start
19441             dds.refreshCache();
19442         }
19443         
19444         var xy = Roo.lib.Event.getXY(e);
19445         var pt = new Roo.lib.Point(xy[0], xy[1]);
19446         for(var id in els){
19447             var el = els[id], r = el._region;
19448             if(r && r.contains(pt) && el.isScrollable()){
19449                 if(r.bottom - pt.y <= dds.thresh){
19450                     if(proc.el != el){
19451                         startProc(el, "down");
19452                     }
19453                     return;
19454                 }else if(r.right - pt.x <= dds.thresh){
19455                     if(proc.el != el){
19456                         startProc(el, "left");
19457                     }
19458                     return;
19459                 }else if(pt.y - r.top <= dds.thresh){
19460                     if(proc.el != el){
19461                         startProc(el, "up");
19462                     }
19463                     return;
19464                 }else if(pt.x - r.left <= dds.thresh){
19465                     if(proc.el != el){
19466                         startProc(el, "right");
19467                     }
19468                     return;
19469                 }
19470             }
19471         }
19472         clearProc();
19473     };
19474     
19475     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19476     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19477     
19478     return {
19479         /**
19480          * Registers new overflow element(s) to auto scroll
19481          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19482          */
19483         register : function(el){
19484             if(el instanceof Array){
19485                 for(var i = 0, len = el.length; i < len; i++) {
19486                         this.register(el[i]);
19487                 }
19488             }else{
19489                 el = Roo.get(el);
19490                 els[el.id] = el;
19491             }
19492             Roo.dd.ScrollManager.els = els;
19493         },
19494         
19495         /**
19496          * Unregisters overflow element(s) so they are no longer scrolled
19497          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19498          */
19499         unregister : function(el){
19500             if(el instanceof Array){
19501                 for(var i = 0, len = el.length; i < len; i++) {
19502                         this.unregister(el[i]);
19503                 }
19504             }else{
19505                 el = Roo.get(el);
19506                 delete els[el.id];
19507             }
19508         },
19509         
19510         /**
19511          * The number of pixels from the edge of a container the pointer needs to be to 
19512          * trigger scrolling (defaults to 25)
19513          * @type Number
19514          */
19515         thresh : 25,
19516         
19517         /**
19518          * The number of pixels to scroll in each scroll increment (defaults to 50)
19519          * @type Number
19520          */
19521         increment : 100,
19522         
19523         /**
19524          * The frequency of scrolls in milliseconds (defaults to 500)
19525          * @type Number
19526          */
19527         frequency : 500,
19528         
19529         /**
19530          * True to animate the scroll (defaults to true)
19531          * @type Boolean
19532          */
19533         animate: true,
19534         
19535         /**
19536          * The animation duration in seconds - 
19537          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19538          * @type Number
19539          */
19540         animDuration: .4,
19541         
19542         /**
19543          * Manually trigger a cache refresh.
19544          */
19545         refreshCache : function(){
19546             for(var id in els){
19547                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19548                     els[id]._region = els[id].getRegion();
19549                 }
19550             }
19551         }
19552     };
19553 }();/*
19554  * Based on:
19555  * Ext JS Library 1.1.1
19556  * Copyright(c) 2006-2007, Ext JS, LLC.
19557  *
19558  * Originally Released Under LGPL - original licence link has changed is not relivant.
19559  *
19560  * Fork - LGPL
19561  * <script type="text/javascript">
19562  */
19563  
19564
19565 /**
19566  * @class Roo.dd.Registry
19567  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19568  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19569  * @singleton
19570  */
19571 Roo.dd.Registry = function(){
19572     var elements = {}; 
19573     var handles = {}; 
19574     var autoIdSeed = 0;
19575
19576     var getId = function(el, autogen){
19577         if(typeof el == "string"){
19578             return el;
19579         }
19580         var id = el.id;
19581         if(!id && autogen !== false){
19582             id = "roodd-" + (++autoIdSeed);
19583             el.id = id;
19584         }
19585         return id;
19586     };
19587     
19588     return {
19589     /**
19590      * Register a drag drop element
19591      * @param {String|HTMLElement} element The id or DOM node to register
19592      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19593      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19594      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19595      * populated in the data object (if applicable):
19596      * <pre>
19597 Value      Description<br />
19598 ---------  ------------------------------------------<br />
19599 handles    Array of DOM nodes that trigger dragging<br />
19600            for the element being registered<br />
19601 isHandle   True if the element passed in triggers<br />
19602            dragging itself, else false
19603 </pre>
19604      */
19605         register : function(el, data){
19606             data = data || {};
19607             if(typeof el == "string"){
19608                 el = document.getElementById(el);
19609             }
19610             data.ddel = el;
19611             elements[getId(el)] = data;
19612             if(data.isHandle !== false){
19613                 handles[data.ddel.id] = data;
19614             }
19615             if(data.handles){
19616                 var hs = data.handles;
19617                 for(var i = 0, len = hs.length; i < len; i++){
19618                         handles[getId(hs[i])] = data;
19619                 }
19620             }
19621         },
19622
19623     /**
19624      * Unregister a drag drop element
19625      * @param {String|HTMLElement}  element The id or DOM node to unregister
19626      */
19627         unregister : function(el){
19628             var id = getId(el, false);
19629             var data = elements[id];
19630             if(data){
19631                 delete elements[id];
19632                 if(data.handles){
19633                     var hs = data.handles;
19634                     for(var i = 0, len = hs.length; i < len; i++){
19635                         delete handles[getId(hs[i], false)];
19636                     }
19637                 }
19638             }
19639         },
19640
19641     /**
19642      * Returns the handle registered for a DOM Node by id
19643      * @param {String|HTMLElement} id The DOM node or id to look up
19644      * @return {Object} handle The custom handle data
19645      */
19646         getHandle : function(id){
19647             if(typeof id != "string"){ // must be element?
19648                 id = id.id;
19649             }
19650             return handles[id];
19651         },
19652
19653     /**
19654      * Returns the handle that is registered for the DOM node that is the target of the event
19655      * @param {Event} e The event
19656      * @return {Object} handle The custom handle data
19657      */
19658         getHandleFromEvent : function(e){
19659             var t = Roo.lib.Event.getTarget(e);
19660             return t ? handles[t.id] : null;
19661         },
19662
19663     /**
19664      * Returns a custom data object that is registered for a DOM node by id
19665      * @param {String|HTMLElement} id The DOM node or id to look up
19666      * @return {Object} data The custom data
19667      */
19668         getTarget : function(id){
19669             if(typeof id != "string"){ // must be element?
19670                 id = id.id;
19671             }
19672             return elements[id];
19673         },
19674
19675     /**
19676      * Returns a custom data object that is registered for the DOM node that is the target of the event
19677      * @param {Event} e The event
19678      * @return {Object} data The custom data
19679      */
19680         getTargetFromEvent : function(e){
19681             var t = Roo.lib.Event.getTarget(e);
19682             return t ? elements[t.id] || handles[t.id] : null;
19683         }
19684     };
19685 }();/*
19686  * Based on:
19687  * Ext JS Library 1.1.1
19688  * Copyright(c) 2006-2007, Ext JS, LLC.
19689  *
19690  * Originally Released Under LGPL - original licence link has changed is not relivant.
19691  *
19692  * Fork - LGPL
19693  * <script type="text/javascript">
19694  */
19695  
19696
19697 /**
19698  * @class Roo.dd.StatusProxy
19699  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19700  * default drag proxy used by all Roo.dd components.
19701  * @constructor
19702  * @param {Object} config
19703  */
19704 Roo.dd.StatusProxy = function(config){
19705     Roo.apply(this, config);
19706     this.id = this.id || Roo.id();
19707     this.el = new Roo.Layer({
19708         dh: {
19709             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19710                 {tag: "div", cls: "x-dd-drop-icon"},
19711                 {tag: "div", cls: "x-dd-drag-ghost"}
19712             ]
19713         }, 
19714         shadow: !config || config.shadow !== false
19715     });
19716     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19717     this.dropStatus = this.dropNotAllowed;
19718 };
19719
19720 Roo.dd.StatusProxy.prototype = {
19721     /**
19722      * @cfg {String} dropAllowed
19723      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19724      */
19725     dropAllowed : "x-dd-drop-ok",
19726     /**
19727      * @cfg {String} dropNotAllowed
19728      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19729      */
19730     dropNotAllowed : "x-dd-drop-nodrop",
19731
19732     /**
19733      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19734      * over the current target element.
19735      * @param {String} cssClass The css class for the new drop status indicator image
19736      */
19737     setStatus : function(cssClass){
19738         cssClass = cssClass || this.dropNotAllowed;
19739         if(this.dropStatus != cssClass){
19740             this.el.replaceClass(this.dropStatus, cssClass);
19741             this.dropStatus = cssClass;
19742         }
19743     },
19744
19745     /**
19746      * Resets the status indicator to the default dropNotAllowed value
19747      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19748      */
19749     reset : function(clearGhost){
19750         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19751         this.dropStatus = this.dropNotAllowed;
19752         if(clearGhost){
19753             this.ghost.update("");
19754         }
19755     },
19756
19757     /**
19758      * Updates the contents of the ghost element
19759      * @param {String} html The html that will replace the current innerHTML of the ghost element
19760      */
19761     update : function(html){
19762         if(typeof html == "string"){
19763             this.ghost.update(html);
19764         }else{
19765             this.ghost.update("");
19766             html.style.margin = "0";
19767             this.ghost.dom.appendChild(html);
19768         }
19769         // ensure float = none set?? cant remember why though.
19770         var el = this.ghost.dom.firstChild;
19771                 if(el){
19772                         Roo.fly(el).setStyle('float', 'none');
19773                 }
19774     },
19775     
19776     /**
19777      * Returns the underlying proxy {@link Roo.Layer}
19778      * @return {Roo.Layer} el
19779     */
19780     getEl : function(){
19781         return this.el;
19782     },
19783
19784     /**
19785      * Returns the ghost element
19786      * @return {Roo.Element} el
19787      */
19788     getGhost : function(){
19789         return this.ghost;
19790     },
19791
19792     /**
19793      * Hides the proxy
19794      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19795      */
19796     hide : function(clear){
19797         this.el.hide();
19798         if(clear){
19799             this.reset(true);
19800         }
19801     },
19802
19803     /**
19804      * Stops the repair animation if it's currently running
19805      */
19806     stop : function(){
19807         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19808             this.anim.stop();
19809         }
19810     },
19811
19812     /**
19813      * Displays this proxy
19814      */
19815     show : function(){
19816         this.el.show();
19817     },
19818
19819     /**
19820      * Force the Layer to sync its shadow and shim positions to the element
19821      */
19822     sync : function(){
19823         this.el.sync();
19824     },
19825
19826     /**
19827      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19828      * invalid drop operation by the item being dragged.
19829      * @param {Array} xy The XY position of the element ([x, y])
19830      * @param {Function} callback The function to call after the repair is complete
19831      * @param {Object} scope The scope in which to execute the callback
19832      */
19833     repair : function(xy, callback, scope){
19834         this.callback = callback;
19835         this.scope = scope;
19836         if(xy && this.animRepair !== false){
19837             this.el.addClass("x-dd-drag-repair");
19838             this.el.hideUnders(true);
19839             this.anim = this.el.shift({
19840                 duration: this.repairDuration || .5,
19841                 easing: 'easeOut',
19842                 xy: xy,
19843                 stopFx: true,
19844                 callback: this.afterRepair,
19845                 scope: this
19846             });
19847         }else{
19848             this.afterRepair();
19849         }
19850     },
19851
19852     // private
19853     afterRepair : function(){
19854         this.hide(true);
19855         if(typeof this.callback == "function"){
19856             this.callback.call(this.scope || this);
19857         }
19858         this.callback = null;
19859         this.scope = null;
19860     }
19861 };/*
19862  * Based on:
19863  * Ext JS Library 1.1.1
19864  * Copyright(c) 2006-2007, Ext JS, LLC.
19865  *
19866  * Originally Released Under LGPL - original licence link has changed is not relivant.
19867  *
19868  * Fork - LGPL
19869  * <script type="text/javascript">
19870  */
19871
19872 /**
19873  * @class Roo.dd.DragSource
19874  * @extends Roo.dd.DDProxy
19875  * A simple class that provides the basic implementation needed to make any element draggable.
19876  * @constructor
19877  * @param {String/HTMLElement/Element} el The container element
19878  * @param {Object} config
19879  */
19880 Roo.dd.DragSource = function(el, config){
19881     this.el = Roo.get(el);
19882     this.dragData = {};
19883     
19884     Roo.apply(this, config);
19885     
19886     if(!this.proxy){
19887         this.proxy = new Roo.dd.StatusProxy();
19888     }
19889
19890     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19891           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19892     
19893     this.dragging = false;
19894 };
19895
19896 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19897     /**
19898      * @cfg {String} dropAllowed
19899      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19900      */
19901     dropAllowed : "x-dd-drop-ok",
19902     /**
19903      * @cfg {String} dropNotAllowed
19904      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19905      */
19906     dropNotAllowed : "x-dd-drop-nodrop",
19907
19908     /**
19909      * Returns the data object associated with this drag source
19910      * @return {Object} data An object containing arbitrary data
19911      */
19912     getDragData : function(e){
19913         return this.dragData;
19914     },
19915
19916     // private
19917     onDragEnter : function(e, id){
19918         var target = Roo.dd.DragDropMgr.getDDById(id);
19919         this.cachedTarget = target;
19920         if(this.beforeDragEnter(target, e, id) !== false){
19921             if(target.isNotifyTarget){
19922                 var status = target.notifyEnter(this, e, this.dragData);
19923                 this.proxy.setStatus(status);
19924             }else{
19925                 this.proxy.setStatus(this.dropAllowed);
19926             }
19927             
19928             if(this.afterDragEnter){
19929                 /**
19930                  * An empty function by default, but provided so that you can perform a custom action
19931                  * when the dragged item enters the drop target by providing an implementation.
19932                  * @param {Roo.dd.DragDrop} target The drop target
19933                  * @param {Event} e The event object
19934                  * @param {String} id The id of the dragged element
19935                  * @method afterDragEnter
19936                  */
19937                 this.afterDragEnter(target, e, id);
19938             }
19939         }
19940     },
19941
19942     /**
19943      * An empty function by default, but provided so that you can perform a custom action
19944      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19945      * @param {Roo.dd.DragDrop} target The drop target
19946      * @param {Event} e The event object
19947      * @param {String} id The id of the dragged element
19948      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19949      */
19950     beforeDragEnter : function(target, e, id){
19951         return true;
19952     },
19953
19954     // private
19955     alignElWithMouse: function() {
19956         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19957         this.proxy.sync();
19958     },
19959
19960     // private
19961     onDragOver : function(e, id){
19962         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19963         if(this.beforeDragOver(target, e, id) !== false){
19964             if(target.isNotifyTarget){
19965                 var status = target.notifyOver(this, e, this.dragData);
19966                 this.proxy.setStatus(status);
19967             }
19968
19969             if(this.afterDragOver){
19970                 /**
19971                  * An empty function by default, but provided so that you can perform a custom action
19972                  * while the dragged item is over the drop target by providing an implementation.
19973                  * @param {Roo.dd.DragDrop} target The drop target
19974                  * @param {Event} e The event object
19975                  * @param {String} id The id of the dragged element
19976                  * @method afterDragOver
19977                  */
19978                 this.afterDragOver(target, e, id);
19979             }
19980         }
19981     },
19982
19983     /**
19984      * An empty function by default, but provided so that you can perform a custom action
19985      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19986      * @param {Roo.dd.DragDrop} target The drop target
19987      * @param {Event} e The event object
19988      * @param {String} id The id of the dragged element
19989      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19990      */
19991     beforeDragOver : function(target, e, id){
19992         return true;
19993     },
19994
19995     // private
19996     onDragOut : function(e, id){
19997         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19998         if(this.beforeDragOut(target, e, id) !== false){
19999             if(target.isNotifyTarget){
20000                 target.notifyOut(this, e, this.dragData);
20001             }
20002             this.proxy.reset();
20003             if(this.afterDragOut){
20004                 /**
20005                  * An empty function by default, but provided so that you can perform a custom action
20006                  * after the dragged item is dragged out of the target without dropping.
20007                  * @param {Roo.dd.DragDrop} target The drop target
20008                  * @param {Event} e The event object
20009                  * @param {String} id The id of the dragged element
20010                  * @method afterDragOut
20011                  */
20012                 this.afterDragOut(target, e, id);
20013             }
20014         }
20015         this.cachedTarget = null;
20016     },
20017
20018     /**
20019      * An empty function by default, but provided so that you can perform a custom action before the dragged
20020      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20021      * @param {Roo.dd.DragDrop} target The drop target
20022      * @param {Event} e The event object
20023      * @param {String} id The id of the dragged element
20024      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20025      */
20026     beforeDragOut : function(target, e, id){
20027         return true;
20028     },
20029     
20030     // private
20031     onDragDrop : function(e, id){
20032         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20033         if(this.beforeDragDrop(target, e, id) !== false){
20034             if(target.isNotifyTarget){
20035                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20036                     this.onValidDrop(target, e, id);
20037                 }else{
20038                     this.onInvalidDrop(target, e, id);
20039                 }
20040             }else{
20041                 this.onValidDrop(target, e, id);
20042             }
20043             
20044             if(this.afterDragDrop){
20045                 /**
20046                  * An empty function by default, but provided so that you can perform a custom action
20047                  * after a valid drag drop has occurred by providing an implementation.
20048                  * @param {Roo.dd.DragDrop} target The drop target
20049                  * @param {Event} e The event object
20050                  * @param {String} id The id of the dropped element
20051                  * @method afterDragDrop
20052                  */
20053                 this.afterDragDrop(target, e, id);
20054             }
20055         }
20056         delete this.cachedTarget;
20057     },
20058
20059     /**
20060      * An empty function by default, but provided so that you can perform a custom action before the dragged
20061      * item is dropped onto the target and optionally cancel the onDragDrop.
20062      * @param {Roo.dd.DragDrop} target The drop target
20063      * @param {Event} e The event object
20064      * @param {String} id The id of the dragged element
20065      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20066      */
20067     beforeDragDrop : function(target, e, id){
20068         return true;
20069     },
20070
20071     // private
20072     onValidDrop : function(target, e, id){
20073         this.hideProxy();
20074         if(this.afterValidDrop){
20075             /**
20076              * An empty function by default, but provided so that you can perform a custom action
20077              * after a valid drop has occurred by providing an implementation.
20078              * @param {Object} target The target DD 
20079              * @param {Event} e The event object
20080              * @param {String} id The id of the dropped element
20081              * @method afterInvalidDrop
20082              */
20083             this.afterValidDrop(target, e, id);
20084         }
20085     },
20086
20087     // private
20088     getRepairXY : function(e, data){
20089         return this.el.getXY();  
20090     },
20091
20092     // private
20093     onInvalidDrop : function(target, e, id){
20094         this.beforeInvalidDrop(target, e, id);
20095         if(this.cachedTarget){
20096             if(this.cachedTarget.isNotifyTarget){
20097                 this.cachedTarget.notifyOut(this, e, this.dragData);
20098             }
20099             this.cacheTarget = null;
20100         }
20101         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20102
20103         if(this.afterInvalidDrop){
20104             /**
20105              * An empty function by default, but provided so that you can perform a custom action
20106              * after an invalid drop has occurred by providing an implementation.
20107              * @param {Event} e The event object
20108              * @param {String} id The id of the dropped element
20109              * @method afterInvalidDrop
20110              */
20111             this.afterInvalidDrop(e, id);
20112         }
20113     },
20114
20115     // private
20116     afterRepair : function(){
20117         if(Roo.enableFx){
20118             this.el.highlight(this.hlColor || "c3daf9");
20119         }
20120         this.dragging = false;
20121     },
20122
20123     /**
20124      * An empty function by default, but provided so that you can perform a custom action after an invalid
20125      * drop has occurred.
20126      * @param {Roo.dd.DragDrop} target The drop target
20127      * @param {Event} e The event object
20128      * @param {String} id The id of the dragged element
20129      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20130      */
20131     beforeInvalidDrop : function(target, e, id){
20132         return true;
20133     },
20134
20135     // private
20136     handleMouseDown : function(e){
20137         if(this.dragging) {
20138             return;
20139         }
20140         var data = this.getDragData(e);
20141         if(data && this.onBeforeDrag(data, e) !== false){
20142             this.dragData = data;
20143             this.proxy.stop();
20144             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20145         } 
20146     },
20147
20148     /**
20149      * An empty function by default, but provided so that you can perform a custom action before the initial
20150      * drag event begins and optionally cancel it.
20151      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20152      * @param {Event} e The event object
20153      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20154      */
20155     onBeforeDrag : function(data, e){
20156         return true;
20157     },
20158
20159     /**
20160      * An empty function by default, but provided so that you can perform a custom action once the initial
20161      * drag event has begun.  The drag cannot be canceled from this function.
20162      * @param {Number} x The x position of the click on the dragged object
20163      * @param {Number} y The y position of the click on the dragged object
20164      */
20165     onStartDrag : Roo.emptyFn,
20166
20167     // private - YUI override
20168     startDrag : function(x, y){
20169         this.proxy.reset();
20170         this.dragging = true;
20171         this.proxy.update("");
20172         this.onInitDrag(x, y);
20173         this.proxy.show();
20174     },
20175
20176     // private
20177     onInitDrag : function(x, y){
20178         var clone = this.el.dom.cloneNode(true);
20179         clone.id = Roo.id(); // prevent duplicate ids
20180         this.proxy.update(clone);
20181         this.onStartDrag(x, y);
20182         return true;
20183     },
20184
20185     /**
20186      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20187      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20188      */
20189     getProxy : function(){
20190         return this.proxy;  
20191     },
20192
20193     /**
20194      * Hides the drag source's {@link Roo.dd.StatusProxy}
20195      */
20196     hideProxy : function(){
20197         this.proxy.hide();  
20198         this.proxy.reset(true);
20199         this.dragging = false;
20200     },
20201
20202     // private
20203     triggerCacheRefresh : function(){
20204         Roo.dd.DDM.refreshCache(this.groups);
20205     },
20206
20207     // private - override to prevent hiding
20208     b4EndDrag: function(e) {
20209     },
20210
20211     // private - override to prevent moving
20212     endDrag : function(e){
20213         this.onEndDrag(this.dragData, e);
20214     },
20215
20216     // private
20217     onEndDrag : function(data, e){
20218     },
20219     
20220     // private - pin to cursor
20221     autoOffset : function(x, y) {
20222         this.setDelta(-12, -20);
20223     }    
20224 });/*
20225  * Based on:
20226  * Ext JS Library 1.1.1
20227  * Copyright(c) 2006-2007, Ext JS, LLC.
20228  *
20229  * Originally Released Under LGPL - original licence link has changed is not relivant.
20230  *
20231  * Fork - LGPL
20232  * <script type="text/javascript">
20233  */
20234
20235
20236 /**
20237  * @class Roo.dd.DropTarget
20238  * @extends Roo.dd.DDTarget
20239  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20240  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20241  * @constructor
20242  * @param {String/HTMLElement/Element} el The container element
20243  * @param {Object} config
20244  */
20245 Roo.dd.DropTarget = function(el, config){
20246     this.el = Roo.get(el);
20247     
20248     var listeners = false; ;
20249     if (config && config.listeners) {
20250         listeners= config.listeners;
20251         delete config.listeners;
20252     }
20253     Roo.apply(this, config);
20254     
20255     if(this.containerScroll){
20256         Roo.dd.ScrollManager.register(this.el);
20257     }
20258     this.addEvents( {
20259          /**
20260          * @scope Roo.dd.DropTarget
20261          */
20262          
20263          /**
20264          * @event enter
20265          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20266          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20267          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20268          * 
20269          * IMPORTANT : it should set this.overClass and this.dropAllowed
20270          * 
20271          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20272          * @param {Event} e The event
20273          * @param {Object} data An object containing arbitrary data supplied by the drag source
20274          */
20275         "enter" : true,
20276         
20277          /**
20278          * @event over
20279          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20280          * This method will be called on every mouse movement while the drag source is over the drop target.
20281          * This default implementation simply returns the dropAllowed config value.
20282          * 
20283          * IMPORTANT : it should set this.dropAllowed
20284          * 
20285          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20286          * @param {Event} e The event
20287          * @param {Object} data An object containing arbitrary data supplied by the drag source
20288          
20289          */
20290         "over" : true,
20291         /**
20292          * @event out
20293          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20294          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20295          * overClass (if any) from the drop element.
20296          * 
20297          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20298          * @param {Event} e The event
20299          * @param {Object} data An object containing arbitrary data supplied by the drag source
20300          */
20301          "out" : true,
20302          
20303         /**
20304          * @event drop
20305          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20306          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20307          * implementation that does something to process the drop event and returns true so that the drag source's
20308          * repair action does not run.
20309          * 
20310          * IMPORTANT : it should set this.success
20311          * 
20312          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20313          * @param {Event} e The event
20314          * @param {Object} data An object containing arbitrary data supplied by the drag source
20315         */
20316          "drop" : true
20317     });
20318             
20319      
20320     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20321         this.el.dom, 
20322         this.ddGroup || this.group,
20323         {
20324             isTarget: true,
20325             listeners : listeners || {} 
20326            
20327         
20328         }
20329     );
20330
20331 };
20332
20333 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20334     /**
20335      * @cfg {String} overClass
20336      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20337      */
20338      /**
20339      * @cfg {String} ddGroup
20340      * The drag drop group to handle drop events for
20341      */
20342      
20343     /**
20344      * @cfg {String} dropAllowed
20345      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20346      */
20347     dropAllowed : "x-dd-drop-ok",
20348     /**
20349      * @cfg {String} dropNotAllowed
20350      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20351      */
20352     dropNotAllowed : "x-dd-drop-nodrop",
20353     /**
20354      * @cfg {boolean} success
20355      * set this after drop listener.. 
20356      */
20357     success : false,
20358     /**
20359      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20360      * if the drop point is valid for over/enter..
20361      */
20362     valid : false,
20363     // private
20364     isTarget : true,
20365
20366     // private
20367     isNotifyTarget : true,
20368     
20369     /**
20370      * @hide
20371      */
20372     notifyEnter : function(dd, e, data)
20373     {
20374         this.valid = true;
20375         this.fireEvent('enter', dd, e, data);
20376         if(this.overClass){
20377             this.el.addClass(this.overClass);
20378         }
20379         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20380             this.valid ? this.dropAllowed : this.dropNotAllowed
20381         );
20382     },
20383
20384     /**
20385      * @hide
20386      */
20387     notifyOver : function(dd, e, data)
20388     {
20389         this.valid = true;
20390         this.fireEvent('over', dd, e, data);
20391         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20392             this.valid ? this.dropAllowed : this.dropNotAllowed
20393         );
20394     },
20395
20396     /**
20397      * @hide
20398      */
20399     notifyOut : function(dd, e, data)
20400     {
20401         this.fireEvent('out', dd, e, data);
20402         if(this.overClass){
20403             this.el.removeClass(this.overClass);
20404         }
20405     },
20406
20407     /**
20408      * @hide
20409      */
20410     notifyDrop : function(dd, e, data)
20411     {
20412         this.success = false;
20413         this.fireEvent('drop', dd, e, data);
20414         return this.success;
20415     }
20416 });/*
20417  * Based on:
20418  * Ext JS Library 1.1.1
20419  * Copyright(c) 2006-2007, Ext JS, LLC.
20420  *
20421  * Originally Released Under LGPL - original licence link has changed is not relivant.
20422  *
20423  * Fork - LGPL
20424  * <script type="text/javascript">
20425  */
20426
20427
20428 /**
20429  * @class Roo.dd.DragZone
20430  * @extends Roo.dd.DragSource
20431  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20432  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20433  * @constructor
20434  * @param {String/HTMLElement/Element} el The container element
20435  * @param {Object} config
20436  */
20437 Roo.dd.DragZone = function(el, config){
20438     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20439     if(this.containerScroll){
20440         Roo.dd.ScrollManager.register(this.el);
20441     }
20442 };
20443
20444 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20445     /**
20446      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20447      * for auto scrolling during drag operations.
20448      */
20449     /**
20450      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20451      * method after a failed drop (defaults to "c3daf9" - light blue)
20452      */
20453
20454     /**
20455      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20456      * for a valid target to drag based on the mouse down. Override this method
20457      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20458      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20459      * @param {EventObject} e The mouse down event
20460      * @return {Object} The dragData
20461      */
20462     getDragData : function(e){
20463         return Roo.dd.Registry.getHandleFromEvent(e);
20464     },
20465     
20466     /**
20467      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20468      * this.dragData.ddel
20469      * @param {Number} x The x position of the click on the dragged object
20470      * @param {Number} y The y position of the click on the dragged object
20471      * @return {Boolean} true to continue the drag, false to cancel
20472      */
20473     onInitDrag : function(x, y){
20474         this.proxy.update(this.dragData.ddel.cloneNode(true));
20475         this.onStartDrag(x, y);
20476         return true;
20477     },
20478     
20479     /**
20480      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20481      */
20482     afterRepair : function(){
20483         if(Roo.enableFx){
20484             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20485         }
20486         this.dragging = false;
20487     },
20488
20489     /**
20490      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20491      * the XY of this.dragData.ddel
20492      * @param {EventObject} e The mouse up event
20493      * @return {Array} The xy location (e.g. [100, 200])
20494      */
20495     getRepairXY : function(e){
20496         return Roo.Element.fly(this.dragData.ddel).getXY();  
20497     }
20498 });/*
20499  * Based on:
20500  * Ext JS Library 1.1.1
20501  * Copyright(c) 2006-2007, Ext JS, LLC.
20502  *
20503  * Originally Released Under LGPL - original licence link has changed is not relivant.
20504  *
20505  * Fork - LGPL
20506  * <script type="text/javascript">
20507  */
20508 /**
20509  * @class Roo.dd.DropZone
20510  * @extends Roo.dd.DropTarget
20511  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20512  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20513  * @constructor
20514  * @param {String/HTMLElement/Element} el The container element
20515  * @param {Object} config
20516  */
20517 Roo.dd.DropZone = function(el, config){
20518     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20519 };
20520
20521 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20522     /**
20523      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20524      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20525      * provide your own custom lookup.
20526      * @param {Event} e The event
20527      * @return {Object} data The custom data
20528      */
20529     getTargetFromEvent : function(e){
20530         return Roo.dd.Registry.getTargetFromEvent(e);
20531     },
20532
20533     /**
20534      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20535      * that it has registered.  This method has no default implementation and should be overridden to provide
20536      * node-specific processing if necessary.
20537      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20538      * {@link #getTargetFromEvent} for this node)
20539      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20540      * @param {Event} e The event
20541      * @param {Object} data An object containing arbitrary data supplied by the drag source
20542      */
20543     onNodeEnter : function(n, dd, e, data){
20544         
20545     },
20546
20547     /**
20548      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20549      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20550      * overridden to provide the proper feedback.
20551      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20552      * {@link #getTargetFromEvent} for this node)
20553      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20554      * @param {Event} e The event
20555      * @param {Object} data An object containing arbitrary data supplied by the drag source
20556      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20557      * underlying {@link Roo.dd.StatusProxy} can be updated
20558      */
20559     onNodeOver : function(n, dd, e, data){
20560         return this.dropAllowed;
20561     },
20562
20563     /**
20564      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20565      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20566      * node-specific processing if necessary.
20567      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20568      * {@link #getTargetFromEvent} for this node)
20569      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20570      * @param {Event} e The event
20571      * @param {Object} data An object containing arbitrary data supplied by the drag source
20572      */
20573     onNodeOut : function(n, dd, e, data){
20574         
20575     },
20576
20577     /**
20578      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20579      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20580      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20581      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20582      * {@link #getTargetFromEvent} for this node)
20583      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20584      * @param {Event} e The event
20585      * @param {Object} data An object containing arbitrary data supplied by the drag source
20586      * @return {Boolean} True if the drop was valid, else false
20587      */
20588     onNodeDrop : function(n, dd, e, data){
20589         return false;
20590     },
20591
20592     /**
20593      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20594      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20595      * it should be overridden to provide the proper feedback if necessary.
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     onContainerOver : function(dd, e, data){
20603         return this.dropNotAllowed;
20604     },
20605
20606     /**
20607      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20608      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20609      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20610      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20611      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20612      * @param {Event} e The event
20613      * @param {Object} data An object containing arbitrary data supplied by the drag source
20614      * @return {Boolean} True if the drop was valid, else false
20615      */
20616     onContainerDrop : function(dd, e, data){
20617         return false;
20618     },
20619
20620     /**
20621      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20622      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20623      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20624      * you should override this method and provide a custom implementation.
20625      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20626      * @param {Event} e The event
20627      * @param {Object} data An object containing arbitrary data supplied by the drag source
20628      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20629      * underlying {@link Roo.dd.StatusProxy} can be updated
20630      */
20631     notifyEnter : function(dd, e, data){
20632         return this.dropNotAllowed;
20633     },
20634
20635     /**
20636      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20637      * This method will be called on every mouse movement while the drag source is over the drop zone.
20638      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20639      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20640      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20641      * registered node, it will call {@link #onContainerOver}.
20642      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20643      * @param {Event} e The event
20644      * @param {Object} data An object containing arbitrary data supplied by the drag source
20645      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20646      * underlying {@link Roo.dd.StatusProxy} can be updated
20647      */
20648     notifyOver : function(dd, e, data){
20649         var n = this.getTargetFromEvent(e);
20650         if(!n){ // not over valid drop target
20651             if(this.lastOverNode){
20652                 this.onNodeOut(this.lastOverNode, dd, e, data);
20653                 this.lastOverNode = null;
20654             }
20655             return this.onContainerOver(dd, e, data);
20656         }
20657         if(this.lastOverNode != n){
20658             if(this.lastOverNode){
20659                 this.onNodeOut(this.lastOverNode, dd, e, data);
20660             }
20661             this.onNodeEnter(n, dd, e, data);
20662             this.lastOverNode = n;
20663         }
20664         return this.onNodeOver(n, dd, e, data);
20665     },
20666
20667     /**
20668      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20669      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20670      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20671      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20672      * @param {Event} e The event
20673      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20674      */
20675     notifyOut : function(dd, e, data){
20676         if(this.lastOverNode){
20677             this.onNodeOut(this.lastOverNode, dd, e, data);
20678             this.lastOverNode = null;
20679         }
20680     },
20681
20682     /**
20683      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20684      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20685      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20686      * otherwise it will call {@link #onContainerDrop}.
20687      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20688      * @param {Event} e The event
20689      * @param {Object} data An object containing arbitrary data supplied by the drag source
20690      * @return {Boolean} True if the drop was valid, else false
20691      */
20692     notifyDrop : function(dd, e, data){
20693         if(this.lastOverNode){
20694             this.onNodeOut(this.lastOverNode, dd, e, data);
20695             this.lastOverNode = null;
20696         }
20697         var n = this.getTargetFromEvent(e);
20698         return n ?
20699             this.onNodeDrop(n, dd, e, data) :
20700             this.onContainerDrop(dd, e, data);
20701     },
20702
20703     // private
20704     triggerCacheRefresh : function(){
20705         Roo.dd.DDM.refreshCache(this.groups);
20706     }  
20707 });/*
20708  * Based on:
20709  * Ext JS Library 1.1.1
20710  * Copyright(c) 2006-2007, Ext JS, LLC.
20711  *
20712  * Originally Released Under LGPL - original licence link has changed is not relivant.
20713  *
20714  * Fork - LGPL
20715  * <script type="text/javascript">
20716  */
20717
20718
20719 /**
20720  * @class Roo.data.SortTypes
20721  * @singleton
20722  * Defines the default sorting (casting?) comparison functions used when sorting data.
20723  */
20724 Roo.data.SortTypes = {
20725     /**
20726      * Default sort that does nothing
20727      * @param {Mixed} s The value being converted
20728      * @return {Mixed} The comparison value
20729      */
20730     none : function(s){
20731         return s;
20732     },
20733     
20734     /**
20735      * The regular expression used to strip tags
20736      * @type {RegExp}
20737      * @property
20738      */
20739     stripTagsRE : /<\/?[^>]+>/gi,
20740     
20741     /**
20742      * Strips all HTML tags to sort on text only
20743      * @param {Mixed} s The value being converted
20744      * @return {String} The comparison value
20745      */
20746     asText : function(s){
20747         return String(s).replace(this.stripTagsRE, "");
20748     },
20749     
20750     /**
20751      * Strips all HTML tags to sort on text only - Case insensitive
20752      * @param {Mixed} s The value being converted
20753      * @return {String} The comparison value
20754      */
20755     asUCText : function(s){
20756         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20757     },
20758     
20759     /**
20760      * Case insensitive string
20761      * @param {Mixed} s The value being converted
20762      * @return {String} The comparison value
20763      */
20764     asUCString : function(s) {
20765         return String(s).toUpperCase();
20766     },
20767     
20768     /**
20769      * Date sorting
20770      * @param {Mixed} s The value being converted
20771      * @return {Number} The comparison value
20772      */
20773     asDate : function(s) {
20774         if(!s){
20775             return 0;
20776         }
20777         if(s instanceof Date){
20778             return s.getTime();
20779         }
20780         return Date.parse(String(s));
20781     },
20782     
20783     /**
20784      * Float sorting
20785      * @param {Mixed} s The value being converted
20786      * @return {Float} The comparison value
20787      */
20788     asFloat : function(s) {
20789         var val = parseFloat(String(s).replace(/,/g, ""));
20790         if(isNaN(val)) val = 0;
20791         return val;
20792     },
20793     
20794     /**
20795      * Integer sorting
20796      * @param {Mixed} s The value being converted
20797      * @return {Number} The comparison value
20798      */
20799     asInt : function(s) {
20800         var val = parseInt(String(s).replace(/,/g, ""));
20801         if(isNaN(val)) val = 0;
20802         return val;
20803     }
20804 };/*
20805  * Based on:
20806  * Ext JS Library 1.1.1
20807  * Copyright(c) 2006-2007, Ext JS, LLC.
20808  *
20809  * Originally Released Under LGPL - original licence link has changed is not relivant.
20810  *
20811  * Fork - LGPL
20812  * <script type="text/javascript">
20813  */
20814
20815 /**
20816 * @class Roo.data.Record
20817  * Instances of this class encapsulate both record <em>definition</em> information, and record
20818  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20819  * to access Records cached in an {@link Roo.data.Store} object.<br>
20820  * <p>
20821  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20822  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20823  * objects.<br>
20824  * <p>
20825  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20826  * @constructor
20827  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20828  * {@link #create}. The parameters are the same.
20829  * @param {Array} data An associative Array of data values keyed by the field name.
20830  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20831  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20832  * not specified an integer id is generated.
20833  */
20834 Roo.data.Record = function(data, id){
20835     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20836     this.data = data;
20837 };
20838
20839 /**
20840  * Generate a constructor for a specific record layout.
20841  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20842  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20843  * Each field definition object may contain the following properties: <ul>
20844  * <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,
20845  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20846  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20847  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20848  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20849  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20850  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20851  * this may be omitted.</p></li>
20852  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20853  * <ul><li>auto (Default, implies no conversion)</li>
20854  * <li>string</li>
20855  * <li>int</li>
20856  * <li>float</li>
20857  * <li>boolean</li>
20858  * <li>date</li></ul></p></li>
20859  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20860  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20861  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20862  * by the Reader into an object that will be stored in the Record. It is passed the
20863  * following parameters:<ul>
20864  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20865  * </ul></p></li>
20866  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20867  * </ul>
20868  * <br>usage:<br><pre><code>
20869 var TopicRecord = Roo.data.Record.create(
20870     {name: 'title', mapping: 'topic_title'},
20871     {name: 'author', mapping: 'username'},
20872     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20873     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20874     {name: 'lastPoster', mapping: 'user2'},
20875     {name: 'excerpt', mapping: 'post_text'}
20876 );
20877
20878 var myNewRecord = new TopicRecord({
20879     title: 'Do my job please',
20880     author: 'noobie',
20881     totalPosts: 1,
20882     lastPost: new Date(),
20883     lastPoster: 'Animal',
20884     excerpt: 'No way dude!'
20885 });
20886 myStore.add(myNewRecord);
20887 </code></pre>
20888  * @method create
20889  * @static
20890  */
20891 Roo.data.Record.create = function(o){
20892     var f = function(){
20893         f.superclass.constructor.apply(this, arguments);
20894     };
20895     Roo.extend(f, Roo.data.Record);
20896     var p = f.prototype;
20897     p.fields = new Roo.util.MixedCollection(false, function(field){
20898         return field.name;
20899     });
20900     for(var i = 0, len = o.length; i < len; i++){
20901         p.fields.add(new Roo.data.Field(o[i]));
20902     }
20903     f.getField = function(name){
20904         return p.fields.get(name);  
20905     };
20906     return f;
20907 };
20908
20909 Roo.data.Record.AUTO_ID = 1000;
20910 Roo.data.Record.EDIT = 'edit';
20911 Roo.data.Record.REJECT = 'reject';
20912 Roo.data.Record.COMMIT = 'commit';
20913
20914 Roo.data.Record.prototype = {
20915     /**
20916      * Readonly flag - true if this record has been modified.
20917      * @type Boolean
20918      */
20919     dirty : false,
20920     editing : false,
20921     error: null,
20922     modified: null,
20923
20924     // private
20925     join : function(store){
20926         this.store = store;
20927     },
20928
20929     /**
20930      * Set the named field to the specified value.
20931      * @param {String} name The name of the field to set.
20932      * @param {Object} value The value to set the field to.
20933      */
20934     set : function(name, value){
20935         if(this.data[name] == value){
20936             return;
20937         }
20938         this.dirty = true;
20939         if(!this.modified){
20940             this.modified = {};
20941         }
20942         if(typeof this.modified[name] == 'undefined'){
20943             this.modified[name] = this.data[name];
20944         }
20945         this.data[name] = value;
20946         if(!this.editing && this.store){
20947             this.store.afterEdit(this);
20948         }       
20949     },
20950
20951     /**
20952      * Get the value of the named field.
20953      * @param {String} name The name of the field to get the value of.
20954      * @return {Object} The value of the field.
20955      */
20956     get : function(name){
20957         return this.data[name]; 
20958     },
20959
20960     // private
20961     beginEdit : function(){
20962         this.editing = true;
20963         this.modified = {}; 
20964     },
20965
20966     // private
20967     cancelEdit : function(){
20968         this.editing = false;
20969         delete this.modified;
20970     },
20971
20972     // private
20973     endEdit : function(){
20974         this.editing = false;
20975         if(this.dirty && this.store){
20976             this.store.afterEdit(this);
20977         }
20978     },
20979
20980     /**
20981      * Usually called by the {@link Roo.data.Store} which owns the Record.
20982      * Rejects all changes made to the Record since either creation, or the last commit operation.
20983      * Modified fields are reverted to their original values.
20984      * <p>
20985      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20986      * of reject operations.
20987      */
20988     reject : function(){
20989         var m = this.modified;
20990         for(var n in m){
20991             if(typeof m[n] != "function"){
20992                 this.data[n] = m[n];
20993             }
20994         }
20995         this.dirty = false;
20996         delete this.modified;
20997         this.editing = false;
20998         if(this.store){
20999             this.store.afterReject(this);
21000         }
21001     },
21002
21003     /**
21004      * Usually called by the {@link Roo.data.Store} which owns the Record.
21005      * Commits all changes made to the Record since either creation, or the last commit operation.
21006      * <p>
21007      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21008      * of commit operations.
21009      */
21010     commit : function(){
21011         this.dirty = false;
21012         delete this.modified;
21013         this.editing = false;
21014         if(this.store){
21015             this.store.afterCommit(this);
21016         }
21017     },
21018
21019     // private
21020     hasError : function(){
21021         return this.error != null;
21022     },
21023
21024     // private
21025     clearError : function(){
21026         this.error = null;
21027     },
21028
21029     /**
21030      * Creates a copy of this record.
21031      * @param {String} id (optional) A new record id if you don't want to use this record's id
21032      * @return {Record}
21033      */
21034     copy : function(newId) {
21035         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21036     }
21037 };/*
21038  * Based on:
21039  * Ext JS Library 1.1.1
21040  * Copyright(c) 2006-2007, Ext JS, LLC.
21041  *
21042  * Originally Released Under LGPL - original licence link has changed is not relivant.
21043  *
21044  * Fork - LGPL
21045  * <script type="text/javascript">
21046  */
21047
21048
21049
21050 /**
21051  * @class Roo.data.Store
21052  * @extends Roo.util.Observable
21053  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21054  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21055  * <p>
21056  * 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
21057  * has no knowledge of the format of the data returned by the Proxy.<br>
21058  * <p>
21059  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21060  * instances from the data object. These records are cached and made available through accessor functions.
21061  * @constructor
21062  * Creates a new Store.
21063  * @param {Object} config A config object containing the objects needed for the Store to access data,
21064  * and read the data into Records.
21065  */
21066 Roo.data.Store = function(config){
21067     this.data = new Roo.util.MixedCollection(false);
21068     this.data.getKey = function(o){
21069         return o.id;
21070     };
21071     this.baseParams = {};
21072     // private
21073     this.paramNames = {
21074         "start" : "start",
21075         "limit" : "limit",
21076         "sort" : "sort",
21077         "dir" : "dir",
21078         "multisort" : "_multisort"
21079     };
21080
21081     if(config && config.data){
21082         this.inlineData = config.data;
21083         delete config.data;
21084     }
21085
21086     Roo.apply(this, config);
21087     
21088     if(this.reader){ // reader passed
21089         this.reader = Roo.factory(this.reader, Roo.data);
21090         this.reader.xmodule = this.xmodule || false;
21091         if(!this.recordType){
21092             this.recordType = this.reader.recordType;
21093         }
21094         if(this.reader.onMetaChange){
21095             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21096         }
21097     }
21098
21099     if(this.recordType){
21100         this.fields = this.recordType.prototype.fields;
21101     }
21102     this.modified = [];
21103
21104     this.addEvents({
21105         /**
21106          * @event datachanged
21107          * Fires when the data cache has changed, and a widget which is using this Store
21108          * as a Record cache should refresh its view.
21109          * @param {Store} this
21110          */
21111         datachanged : true,
21112         /**
21113          * @event metachange
21114          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21115          * @param {Store} this
21116          * @param {Object} meta The JSON metadata
21117          */
21118         metachange : true,
21119         /**
21120          * @event add
21121          * Fires when Records have been added to the Store
21122          * @param {Store} this
21123          * @param {Roo.data.Record[]} records The array of Records added
21124          * @param {Number} index The index at which the record(s) were added
21125          */
21126         add : true,
21127         /**
21128          * @event remove
21129          * Fires when a Record has been removed from the Store
21130          * @param {Store} this
21131          * @param {Roo.data.Record} record The Record that was removed
21132          * @param {Number} index The index at which the record was removed
21133          */
21134         remove : true,
21135         /**
21136          * @event update
21137          * Fires when a Record has been updated
21138          * @param {Store} this
21139          * @param {Roo.data.Record} record The Record that was updated
21140          * @param {String} operation The update operation being performed.  Value may be one of:
21141          * <pre><code>
21142  Roo.data.Record.EDIT
21143  Roo.data.Record.REJECT
21144  Roo.data.Record.COMMIT
21145          * </code></pre>
21146          */
21147         update : true,
21148         /**
21149          * @event clear
21150          * Fires when the data cache has been cleared.
21151          * @param {Store} this
21152          */
21153         clear : true,
21154         /**
21155          * @event beforeload
21156          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21157          * the load action will be canceled.
21158          * @param {Store} this
21159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21160          */
21161         beforeload : true,
21162         /**
21163          * @event beforeloadadd
21164          * Fires after a new set of Records has been loaded.
21165          * @param {Store} this
21166          * @param {Roo.data.Record[]} records The Records that were loaded
21167          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21168          */
21169         beforeloadadd : true,
21170         /**
21171          * @event load
21172          * Fires after a new set of Records has been loaded, before they are added to the store.
21173          * @param {Store} this
21174          * @param {Roo.data.Record[]} records The Records that were loaded
21175          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21176          * @params {Object} return from reader
21177          */
21178         load : true,
21179         /**
21180          * @event loadexception
21181          * Fires if an exception occurs in the Proxy during loading.
21182          * Called with the signature of the Proxy's "loadexception" event.
21183          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21184          * 
21185          * @param {Proxy} 
21186          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21187          * @param {Object} load options 
21188          * @param {Object} jsonData from your request (normally this contains the Exception)
21189          */
21190         loadexception : true
21191     });
21192     
21193     if(this.proxy){
21194         this.proxy = Roo.factory(this.proxy, Roo.data);
21195         this.proxy.xmodule = this.xmodule || false;
21196         this.relayEvents(this.proxy,  ["loadexception"]);
21197     }
21198     this.sortToggle = {};
21199     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21200
21201     Roo.data.Store.superclass.constructor.call(this);
21202
21203     if(this.inlineData){
21204         this.loadData(this.inlineData);
21205         delete this.inlineData;
21206     }
21207 };
21208
21209 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21210      /**
21211     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21212     * without a remote query - used by combo/forms at present.
21213     */
21214     
21215     /**
21216     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21217     */
21218     /**
21219     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21220     */
21221     /**
21222     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21223     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21224     */
21225     /**
21226     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21227     * on any HTTP request
21228     */
21229     /**
21230     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21231     */
21232     /**
21233     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21234     */
21235     multiSort: false,
21236     /**
21237     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21238     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21239     */
21240     remoteSort : false,
21241
21242     /**
21243     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21244      * loaded or when a record is removed. (defaults to false).
21245     */
21246     pruneModifiedRecords : false,
21247
21248     // private
21249     lastOptions : null,
21250
21251     /**
21252      * Add Records to the Store and fires the add event.
21253      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21254      */
21255     add : function(records){
21256         records = [].concat(records);
21257         for(var i = 0, len = records.length; i < len; i++){
21258             records[i].join(this);
21259         }
21260         var index = this.data.length;
21261         this.data.addAll(records);
21262         this.fireEvent("add", this, records, index);
21263     },
21264
21265     /**
21266      * Remove a Record from the Store and fires the remove event.
21267      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21268      */
21269     remove : function(record){
21270         var index = this.data.indexOf(record);
21271         this.data.removeAt(index);
21272         if(this.pruneModifiedRecords){
21273             this.modified.remove(record);
21274         }
21275         this.fireEvent("remove", this, record, index);
21276     },
21277
21278     /**
21279      * Remove all Records from the Store and fires the clear event.
21280      */
21281     removeAll : function(){
21282         this.data.clear();
21283         if(this.pruneModifiedRecords){
21284             this.modified = [];
21285         }
21286         this.fireEvent("clear", this);
21287     },
21288
21289     /**
21290      * Inserts Records to the Store at the given index and fires the add event.
21291      * @param {Number} index The start index at which to insert the passed Records.
21292      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21293      */
21294     insert : function(index, records){
21295         records = [].concat(records);
21296         for(var i = 0, len = records.length; i < len; i++){
21297             this.data.insert(index, records[i]);
21298             records[i].join(this);
21299         }
21300         this.fireEvent("add", this, records, index);
21301     },
21302
21303     /**
21304      * Get the index within the cache of the passed Record.
21305      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21306      * @return {Number} The index of the passed Record. Returns -1 if not found.
21307      */
21308     indexOf : function(record){
21309         return this.data.indexOf(record);
21310     },
21311
21312     /**
21313      * Get the index within the cache of the Record with the passed id.
21314      * @param {String} id The id of the Record to find.
21315      * @return {Number} The index of the Record. Returns -1 if not found.
21316      */
21317     indexOfId : function(id){
21318         return this.data.indexOfKey(id);
21319     },
21320
21321     /**
21322      * Get the Record with the specified id.
21323      * @param {String} id The id of the Record to find.
21324      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21325      */
21326     getById : function(id){
21327         return this.data.key(id);
21328     },
21329
21330     /**
21331      * Get the Record at the specified index.
21332      * @param {Number} index The index of the Record to find.
21333      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21334      */
21335     getAt : function(index){
21336         return this.data.itemAt(index);
21337     },
21338
21339     /**
21340      * Returns a range of Records between specified indices.
21341      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21342      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21343      * @return {Roo.data.Record[]} An array of Records
21344      */
21345     getRange : function(start, end){
21346         return this.data.getRange(start, end);
21347     },
21348
21349     // private
21350     storeOptions : function(o){
21351         o = Roo.apply({}, o);
21352         delete o.callback;
21353         delete o.scope;
21354         this.lastOptions = o;
21355     },
21356
21357     /**
21358      * Loads the Record cache from the configured Proxy using the configured Reader.
21359      * <p>
21360      * If using remote paging, then the first load call must specify the <em>start</em>
21361      * and <em>limit</em> properties in the options.params property to establish the initial
21362      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21363      * <p>
21364      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21365      * and this call will return before the new data has been loaded. Perform any post-processing
21366      * in a callback function, or in a "load" event handler.</strong>
21367      * <p>
21368      * @param {Object} options An object containing properties which control loading options:<ul>
21369      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21370      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21371      * passed the following arguments:<ul>
21372      * <li>r : Roo.data.Record[]</li>
21373      * <li>options: Options object from the load call</li>
21374      * <li>success: Boolean success indicator</li></ul></li>
21375      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21376      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21377      * </ul>
21378      */
21379     load : function(options){
21380         options = options || {};
21381         if(this.fireEvent("beforeload", this, options) !== false){
21382             this.storeOptions(options);
21383             var p = Roo.apply(options.params || {}, this.baseParams);
21384             // if meta was not loaded from remote source.. try requesting it.
21385             if (!this.reader.metaFromRemote) {
21386                 p._requestMeta = 1;
21387             }
21388             if(this.sortInfo && this.remoteSort){
21389                 var pn = this.paramNames;
21390                 p[pn["sort"]] = this.sortInfo.field;
21391                 p[pn["dir"]] = this.sortInfo.direction;
21392             }
21393             if (this.multiSort) {
21394                 var pn = this.paramNames;
21395                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21396             }
21397             
21398             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21399         }
21400     },
21401
21402     /**
21403      * Reloads the Record cache from the configured Proxy using the configured Reader and
21404      * the options from the last load operation performed.
21405      * @param {Object} options (optional) An object containing properties which may override the options
21406      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21407      * the most recently used options are reused).
21408      */
21409     reload : function(options){
21410         this.load(Roo.applyIf(options||{}, this.lastOptions));
21411     },
21412
21413     // private
21414     // Called as a callback by the Reader during a load operation.
21415     loadRecords : function(o, options, success){
21416         if(!o || success === false){
21417             if(success !== false){
21418                 this.fireEvent("load", this, [], options, o);
21419             }
21420             if(options.callback){
21421                 options.callback.call(options.scope || this, [], options, false);
21422             }
21423             return;
21424         }
21425         // if data returned failure - throw an exception.
21426         if (o.success === false) {
21427             // show a message if no listener is registered.
21428             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21429                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21430             }
21431             // loadmask wil be hooked into this..
21432             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21433             return;
21434         }
21435         var r = o.records, t = o.totalRecords || r.length;
21436         
21437         this.fireEvent("beforeloadadd", this, r, options, o);
21438         
21439         if(!options || options.add !== true){
21440             if(this.pruneModifiedRecords){
21441                 this.modified = [];
21442             }
21443             for(var i = 0, len = r.length; i < len; i++){
21444                 r[i].join(this);
21445             }
21446             if(this.snapshot){
21447                 this.data = this.snapshot;
21448                 delete this.snapshot;
21449             }
21450             this.data.clear();
21451             this.data.addAll(r);
21452             this.totalLength = t;
21453             this.applySort();
21454             this.fireEvent("datachanged", this);
21455         }else{
21456             this.totalLength = Math.max(t, this.data.length+r.length);
21457             this.add(r);
21458         }
21459         this.fireEvent("load", this, r, options, o);
21460         if(options.callback){
21461             options.callback.call(options.scope || this, r, options, true);
21462         }
21463     },
21464
21465
21466     /**
21467      * Loads data from a passed data block. A Reader which understands the format of the data
21468      * must have been configured in the constructor.
21469      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21470      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21471      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21472      */
21473     loadData : function(o, append){
21474         var r = this.reader.readRecords(o);
21475         this.loadRecords(r, {add: append}, true);
21476     },
21477
21478     /**
21479      * Gets the number of cached records.
21480      * <p>
21481      * <em>If using paging, this may not be the total size of the dataset. If the data object
21482      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21483      * the data set size</em>
21484      */
21485     getCount : function(){
21486         return this.data.length || 0;
21487     },
21488
21489     /**
21490      * Gets the total number of records in the dataset as returned by the server.
21491      * <p>
21492      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21493      * the dataset size</em>
21494      */
21495     getTotalCount : function(){
21496         return this.totalLength || 0;
21497     },
21498
21499     /**
21500      * Returns the sort state of the Store as an object with two properties:
21501      * <pre><code>
21502  field {String} The name of the field by which the Records are sorted
21503  direction {String} The sort order, "ASC" or "DESC"
21504      * </code></pre>
21505      */
21506     getSortState : function(){
21507         return this.sortInfo;
21508     },
21509
21510     // private
21511     applySort : function(){
21512         if(this.sortInfo && !this.remoteSort){
21513             var s = this.sortInfo, f = s.field;
21514             var st = this.fields.get(f).sortType;
21515             var fn = function(r1, r2){
21516                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21517                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21518             };
21519             this.data.sort(s.direction, fn);
21520             if(this.snapshot && this.snapshot != this.data){
21521                 this.snapshot.sort(s.direction, fn);
21522             }
21523         }
21524     },
21525
21526     /**
21527      * Sets the default sort column and order to be used by the next load operation.
21528      * @param {String} fieldName The name of the field to sort by.
21529      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21530      */
21531     setDefaultSort : function(field, dir){
21532         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21533     },
21534
21535     /**
21536      * Sort the Records.
21537      * If remote sorting is used, the sort is performed on the server, and the cache is
21538      * reloaded. If local sorting is used, the cache is sorted internally.
21539      * @param {String} fieldName The name of the field to sort by.
21540      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21541      */
21542     sort : function(fieldName, dir){
21543         var f = this.fields.get(fieldName);
21544         if(!dir){
21545             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21546             
21547             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21548                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21549             }else{
21550                 dir = f.sortDir;
21551             }
21552         }
21553         this.sortToggle[f.name] = dir;
21554         this.sortInfo = {field: f.name, direction: dir};
21555         if(!this.remoteSort){
21556             this.applySort();
21557             this.fireEvent("datachanged", this);
21558         }else{
21559             this.load(this.lastOptions);
21560         }
21561     },
21562
21563     /**
21564      * Calls the specified function for each of the Records in the cache.
21565      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21566      * Returning <em>false</em> aborts and exits the iteration.
21567      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21568      */
21569     each : function(fn, scope){
21570         this.data.each(fn, scope);
21571     },
21572
21573     /**
21574      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21575      * (e.g., during paging).
21576      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21577      */
21578     getModifiedRecords : function(){
21579         return this.modified;
21580     },
21581
21582     // private
21583     createFilterFn : function(property, value, anyMatch){
21584         if(!value.exec){ // not a regex
21585             value = String(value);
21586             if(value.length == 0){
21587                 return false;
21588             }
21589             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21590         }
21591         return function(r){
21592             return value.test(r.data[property]);
21593         };
21594     },
21595
21596     /**
21597      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21598      * @param {String} property A field on your records
21599      * @param {Number} start The record index to start at (defaults to 0)
21600      * @param {Number} end The last record index to include (defaults to length - 1)
21601      * @return {Number} The sum
21602      */
21603     sum : function(property, start, end){
21604         var rs = this.data.items, v = 0;
21605         start = start || 0;
21606         end = (end || end === 0) ? end : rs.length-1;
21607
21608         for(var i = start; i <= end; i++){
21609             v += (rs[i].data[property] || 0);
21610         }
21611         return v;
21612     },
21613
21614     /**
21615      * Filter the records by a specified property.
21616      * @param {String} field A field on your records
21617      * @param {String/RegExp} value Either a string that the field
21618      * should start with or a RegExp to test against the field
21619      * @param {Boolean} anyMatch True to match any part not just the beginning
21620      */
21621     filter : function(property, value, anyMatch){
21622         var fn = this.createFilterFn(property, value, anyMatch);
21623         return fn ? this.filterBy(fn) : this.clearFilter();
21624     },
21625
21626     /**
21627      * Filter by a function. The specified function will be called with each
21628      * record in this data source. If the function returns true the record is included,
21629      * otherwise it is filtered.
21630      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21631      * @param {Object} scope (optional) The scope of the function (defaults to this)
21632      */
21633     filterBy : function(fn, scope){
21634         this.snapshot = this.snapshot || this.data;
21635         this.data = this.queryBy(fn, scope||this);
21636         this.fireEvent("datachanged", this);
21637     },
21638
21639     /**
21640      * Query the records by a specified property.
21641      * @param {String} field A field on your records
21642      * @param {String/RegExp} value Either a string that the field
21643      * should start with or a RegExp to test against the field
21644      * @param {Boolean} anyMatch True to match any part not just the beginning
21645      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21646      */
21647     query : function(property, value, anyMatch){
21648         var fn = this.createFilterFn(property, value, anyMatch);
21649         return fn ? this.queryBy(fn) : this.data.clone();
21650     },
21651
21652     /**
21653      * Query by a function. The specified function will be called with each
21654      * record in this data source. If the function returns true the record is included
21655      * in the results.
21656      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21657      * @param {Object} scope (optional) The scope of the function (defaults to this)
21658       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21659      **/
21660     queryBy : function(fn, scope){
21661         var data = this.snapshot || this.data;
21662         return data.filterBy(fn, scope||this);
21663     },
21664
21665     /**
21666      * Collects unique values for a particular dataIndex from this store.
21667      * @param {String} dataIndex The property to collect
21668      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21669      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21670      * @return {Array} An array of the unique values
21671      **/
21672     collect : function(dataIndex, allowNull, bypassFilter){
21673         var d = (bypassFilter === true && this.snapshot) ?
21674                 this.snapshot.items : this.data.items;
21675         var v, sv, r = [], l = {};
21676         for(var i = 0, len = d.length; i < len; i++){
21677             v = d[i].data[dataIndex];
21678             sv = String(v);
21679             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21680                 l[sv] = true;
21681                 r[r.length] = v;
21682             }
21683         }
21684         return r;
21685     },
21686
21687     /**
21688      * Revert to a view of the Record cache with no filtering applied.
21689      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21690      */
21691     clearFilter : function(suppressEvent){
21692         if(this.snapshot && this.snapshot != this.data){
21693             this.data = this.snapshot;
21694             delete this.snapshot;
21695             if(suppressEvent !== true){
21696                 this.fireEvent("datachanged", this);
21697             }
21698         }
21699     },
21700
21701     // private
21702     afterEdit : function(record){
21703         if(this.modified.indexOf(record) == -1){
21704             this.modified.push(record);
21705         }
21706         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21707     },
21708     
21709     // private
21710     afterReject : function(record){
21711         this.modified.remove(record);
21712         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21713     },
21714
21715     // private
21716     afterCommit : function(record){
21717         this.modified.remove(record);
21718         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21719     },
21720
21721     /**
21722      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21723      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21724      */
21725     commitChanges : function(){
21726         var m = this.modified.slice(0);
21727         this.modified = [];
21728         for(var i = 0, len = m.length; i < len; i++){
21729             m[i].commit();
21730         }
21731     },
21732
21733     /**
21734      * Cancel outstanding changes on all changed records.
21735      */
21736     rejectChanges : function(){
21737         var m = this.modified.slice(0);
21738         this.modified = [];
21739         for(var i = 0, len = m.length; i < len; i++){
21740             m[i].reject();
21741         }
21742     },
21743
21744     onMetaChange : function(meta, rtype, o){
21745         this.recordType = rtype;
21746         this.fields = rtype.prototype.fields;
21747         delete this.snapshot;
21748         this.sortInfo = meta.sortInfo || this.sortInfo;
21749         this.modified = [];
21750         this.fireEvent('metachange', this, this.reader.meta);
21751     },
21752     
21753     moveIndex : function(data, type)
21754     {
21755         var index = this.indexOf(data);
21756         
21757         var newIndex = index + type;
21758         
21759         this.remove(data);
21760         
21761         this.insert(newIndex, data);
21762         
21763     }
21764 });/*
21765  * Based on:
21766  * Ext JS Library 1.1.1
21767  * Copyright(c) 2006-2007, Ext JS, LLC.
21768  *
21769  * Originally Released Under LGPL - original licence link has changed is not relivant.
21770  *
21771  * Fork - LGPL
21772  * <script type="text/javascript">
21773  */
21774
21775 /**
21776  * @class Roo.data.SimpleStore
21777  * @extends Roo.data.Store
21778  * Small helper class to make creating Stores from Array data easier.
21779  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21780  * @cfg {Array} fields An array of field definition objects, or field name strings.
21781  * @cfg {Array} data The multi-dimensional array of data
21782  * @constructor
21783  * @param {Object} config
21784  */
21785 Roo.data.SimpleStore = function(config){
21786     Roo.data.SimpleStore.superclass.constructor.call(this, {
21787         isLocal : true,
21788         reader: new Roo.data.ArrayReader({
21789                 id: config.id
21790             },
21791             Roo.data.Record.create(config.fields)
21792         ),
21793         proxy : new Roo.data.MemoryProxy(config.data)
21794     });
21795     this.load();
21796 };
21797 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21798  * Based on:
21799  * Ext JS Library 1.1.1
21800  * Copyright(c) 2006-2007, Ext JS, LLC.
21801  *
21802  * Originally Released Under LGPL - original licence link has changed is not relivant.
21803  *
21804  * Fork - LGPL
21805  * <script type="text/javascript">
21806  */
21807
21808 /**
21809 /**
21810  * @extends Roo.data.Store
21811  * @class Roo.data.JsonStore
21812  * Small helper class to make creating Stores for JSON data easier. <br/>
21813 <pre><code>
21814 var store = new Roo.data.JsonStore({
21815     url: 'get-images.php',
21816     root: 'images',
21817     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21818 });
21819 </code></pre>
21820  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21821  * JsonReader and HttpProxy (unless inline data is provided).</b>
21822  * @cfg {Array} fields An array of field definition objects, or field name strings.
21823  * @constructor
21824  * @param {Object} config
21825  */
21826 Roo.data.JsonStore = function(c){
21827     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21828         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21829         reader: new Roo.data.JsonReader(c, c.fields)
21830     }));
21831 };
21832 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21833  * Based on:
21834  * Ext JS Library 1.1.1
21835  * Copyright(c) 2006-2007, Ext JS, LLC.
21836  *
21837  * Originally Released Under LGPL - original licence link has changed is not relivant.
21838  *
21839  * Fork - LGPL
21840  * <script type="text/javascript">
21841  */
21842
21843  
21844 Roo.data.Field = function(config){
21845     if(typeof config == "string"){
21846         config = {name: config};
21847     }
21848     Roo.apply(this, config);
21849     
21850     if(!this.type){
21851         this.type = "auto";
21852     }
21853     
21854     var st = Roo.data.SortTypes;
21855     // named sortTypes are supported, here we look them up
21856     if(typeof this.sortType == "string"){
21857         this.sortType = st[this.sortType];
21858     }
21859     
21860     // set default sortType for strings and dates
21861     if(!this.sortType){
21862         switch(this.type){
21863             case "string":
21864                 this.sortType = st.asUCString;
21865                 break;
21866             case "date":
21867                 this.sortType = st.asDate;
21868                 break;
21869             default:
21870                 this.sortType = st.none;
21871         }
21872     }
21873
21874     // define once
21875     var stripRe = /[\$,%]/g;
21876
21877     // prebuilt conversion function for this field, instead of
21878     // switching every time we're reading a value
21879     if(!this.convert){
21880         var cv, dateFormat = this.dateFormat;
21881         switch(this.type){
21882             case "":
21883             case "auto":
21884             case undefined:
21885                 cv = function(v){ return v; };
21886                 break;
21887             case "string":
21888                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21889                 break;
21890             case "int":
21891                 cv = function(v){
21892                     return v !== undefined && v !== null && v !== '' ?
21893                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21894                     };
21895                 break;
21896             case "float":
21897                 cv = function(v){
21898                     return v !== undefined && v !== null && v !== '' ?
21899                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21900                     };
21901                 break;
21902             case "bool":
21903             case "boolean":
21904                 cv = function(v){ return v === true || v === "true" || v == 1; };
21905                 break;
21906             case "date":
21907                 cv = function(v){
21908                     if(!v){
21909                         return '';
21910                     }
21911                     if(v instanceof Date){
21912                         return v;
21913                     }
21914                     if(dateFormat){
21915                         if(dateFormat == "timestamp"){
21916                             return new Date(v*1000);
21917                         }
21918                         return Date.parseDate(v, dateFormat);
21919                     }
21920                     var parsed = Date.parse(v);
21921                     return parsed ? new Date(parsed) : null;
21922                 };
21923              break;
21924             
21925         }
21926         this.convert = cv;
21927     }
21928 };
21929
21930 Roo.data.Field.prototype = {
21931     dateFormat: null,
21932     defaultValue: "",
21933     mapping: null,
21934     sortType : null,
21935     sortDir : "ASC"
21936 };/*
21937  * Based on:
21938  * Ext JS Library 1.1.1
21939  * Copyright(c) 2006-2007, Ext JS, LLC.
21940  *
21941  * Originally Released Under LGPL - original licence link has changed is not relivant.
21942  *
21943  * Fork - LGPL
21944  * <script type="text/javascript">
21945  */
21946  
21947 // Base class for reading structured data from a data source.  This class is intended to be
21948 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21949
21950 /**
21951  * @class Roo.data.DataReader
21952  * Base class for reading structured data from a data source.  This class is intended to be
21953  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21954  */
21955
21956 Roo.data.DataReader = function(meta, recordType){
21957     
21958     this.meta = meta;
21959     
21960     this.recordType = recordType instanceof Array ? 
21961         Roo.data.Record.create(recordType) : recordType;
21962 };
21963
21964 Roo.data.DataReader.prototype = {
21965      /**
21966      * Create an empty record
21967      * @param {Object} data (optional) - overlay some values
21968      * @return {Roo.data.Record} record created.
21969      */
21970     newRow :  function(d) {
21971         var da =  {};
21972         this.recordType.prototype.fields.each(function(c) {
21973             switch( c.type) {
21974                 case 'int' : da[c.name] = 0; break;
21975                 case 'date' : da[c.name] = new Date(); break;
21976                 case 'float' : da[c.name] = 0.0; break;
21977                 case 'boolean' : da[c.name] = false; break;
21978                 default : da[c.name] = ""; break;
21979             }
21980             
21981         });
21982         return new this.recordType(Roo.apply(da, d));
21983     }
21984     
21985 };/*
21986  * Based on:
21987  * Ext JS Library 1.1.1
21988  * Copyright(c) 2006-2007, Ext JS, LLC.
21989  *
21990  * Originally Released Under LGPL - original licence link has changed is not relivant.
21991  *
21992  * Fork - LGPL
21993  * <script type="text/javascript">
21994  */
21995
21996 /**
21997  * @class Roo.data.DataProxy
21998  * @extends Roo.data.Observable
21999  * This class is an abstract base class for implementations which provide retrieval of
22000  * unformatted data objects.<br>
22001  * <p>
22002  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22003  * (of the appropriate type which knows how to parse the data object) to provide a block of
22004  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22005  * <p>
22006  * Custom implementations must implement the load method as described in
22007  * {@link Roo.data.HttpProxy#load}.
22008  */
22009 Roo.data.DataProxy = function(){
22010     this.addEvents({
22011         /**
22012          * @event beforeload
22013          * Fires before a network request is made to retrieve a data object.
22014          * @param {Object} This DataProxy object.
22015          * @param {Object} params The params parameter to the load function.
22016          */
22017         beforeload : true,
22018         /**
22019          * @event load
22020          * Fires before the load method's callback is called.
22021          * @param {Object} This DataProxy object.
22022          * @param {Object} o The data object.
22023          * @param {Object} arg The callback argument object passed to the load function.
22024          */
22025         load : true,
22026         /**
22027          * @event loadexception
22028          * Fires if an Exception occurs during data retrieval.
22029          * @param {Object} This DataProxy object.
22030          * @param {Object} o The data object.
22031          * @param {Object} arg The callback argument object passed to the load function.
22032          * @param {Object} e The Exception.
22033          */
22034         loadexception : true
22035     });
22036     Roo.data.DataProxy.superclass.constructor.call(this);
22037 };
22038
22039 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22040
22041     /**
22042      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22043      */
22044 /*
22045  * Based on:
22046  * Ext JS Library 1.1.1
22047  * Copyright(c) 2006-2007, Ext JS, LLC.
22048  *
22049  * Originally Released Under LGPL - original licence link has changed is not relivant.
22050  *
22051  * Fork - LGPL
22052  * <script type="text/javascript">
22053  */
22054 /**
22055  * @class Roo.data.MemoryProxy
22056  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22057  * to the Reader when its load method is called.
22058  * @constructor
22059  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22060  */
22061 Roo.data.MemoryProxy = function(data){
22062     if (data.data) {
22063         data = data.data;
22064     }
22065     Roo.data.MemoryProxy.superclass.constructor.call(this);
22066     this.data = data;
22067 };
22068
22069 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22070     /**
22071      * Load data from the requested source (in this case an in-memory
22072      * data object passed to the constructor), read the data object into
22073      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22074      * process that block using the passed callback.
22075      * @param {Object} params This parameter is not used by the MemoryProxy class.
22076      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22077      * object into a block of Roo.data.Records.
22078      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22079      * The function must be passed <ul>
22080      * <li>The Record block object</li>
22081      * <li>The "arg" argument from the load function</li>
22082      * <li>A boolean success indicator</li>
22083      * </ul>
22084      * @param {Object} scope The scope in which to call the callback
22085      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22086      */
22087     load : function(params, reader, callback, scope, arg){
22088         params = params || {};
22089         var result;
22090         try {
22091             result = reader.readRecords(this.data);
22092         }catch(e){
22093             this.fireEvent("loadexception", this, arg, null, e);
22094             callback.call(scope, null, arg, false);
22095             return;
22096         }
22097         callback.call(scope, result, arg, true);
22098     },
22099     
22100     // private
22101     update : function(params, records){
22102         
22103     }
22104 });/*
22105  * Based on:
22106  * Ext JS Library 1.1.1
22107  * Copyright(c) 2006-2007, Ext JS, LLC.
22108  *
22109  * Originally Released Under LGPL - original licence link has changed is not relivant.
22110  *
22111  * Fork - LGPL
22112  * <script type="text/javascript">
22113  */
22114 /**
22115  * @class Roo.data.HttpProxy
22116  * @extends Roo.data.DataProxy
22117  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22118  * configured to reference a certain URL.<br><br>
22119  * <p>
22120  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22121  * from which the running page was served.<br><br>
22122  * <p>
22123  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22124  * <p>
22125  * Be aware that to enable the browser to parse an XML document, the server must set
22126  * the Content-Type header in the HTTP response to "text/xml".
22127  * @constructor
22128  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22129  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22130  * will be used to make the request.
22131  */
22132 Roo.data.HttpProxy = function(conn){
22133     Roo.data.HttpProxy.superclass.constructor.call(this);
22134     // is conn a conn config or a real conn?
22135     this.conn = conn;
22136     this.useAjax = !conn || !conn.events;
22137   
22138 };
22139
22140 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22141     // thse are take from connection...
22142     
22143     /**
22144      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22145      */
22146     /**
22147      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22148      * extra parameters to each request made by this object. (defaults to undefined)
22149      */
22150     /**
22151      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22152      *  to each request made by this object. (defaults to undefined)
22153      */
22154     /**
22155      * @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)
22156      */
22157     /**
22158      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22159      */
22160      /**
22161      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22162      * @type Boolean
22163      */
22164   
22165
22166     /**
22167      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22168      * @type Boolean
22169      */
22170     /**
22171      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22172      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22173      * a finer-grained basis than the DataProxy events.
22174      */
22175     getConnection : function(){
22176         return this.useAjax ? Roo.Ajax : this.conn;
22177     },
22178
22179     /**
22180      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22181      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22182      * process that block using the passed callback.
22183      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22184      * for the request to the remote server.
22185      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22186      * object into a block of Roo.data.Records.
22187      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22188      * The function must be passed <ul>
22189      * <li>The Record block object</li>
22190      * <li>The "arg" argument from the load function</li>
22191      * <li>A boolean success indicator</li>
22192      * </ul>
22193      * @param {Object} scope The scope in which to call the callback
22194      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22195      */
22196     load : function(params, reader, callback, scope, arg){
22197         if(this.fireEvent("beforeload", this, params) !== false){
22198             var  o = {
22199                 params : params || {},
22200                 request: {
22201                     callback : callback,
22202                     scope : scope,
22203                     arg : arg
22204                 },
22205                 reader: reader,
22206                 callback : this.loadResponse,
22207                 scope: this
22208             };
22209             if(this.useAjax){
22210                 Roo.applyIf(o, this.conn);
22211                 if(this.activeRequest){
22212                     Roo.Ajax.abort(this.activeRequest);
22213                 }
22214                 this.activeRequest = Roo.Ajax.request(o);
22215             }else{
22216                 this.conn.request(o);
22217             }
22218         }else{
22219             callback.call(scope||this, null, arg, false);
22220         }
22221     },
22222
22223     // private
22224     loadResponse : function(o, success, response){
22225         delete this.activeRequest;
22226         if(!success){
22227             this.fireEvent("loadexception", this, o, response);
22228             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22229             return;
22230         }
22231         var result;
22232         try {
22233             result = o.reader.read(response);
22234         }catch(e){
22235             this.fireEvent("loadexception", this, o, response, e);
22236             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22237             return;
22238         }
22239         
22240         this.fireEvent("load", this, o, o.request.arg);
22241         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22242     },
22243
22244     // private
22245     update : function(dataSet){
22246
22247     },
22248
22249     // private
22250     updateResponse : function(dataSet){
22251
22252     }
22253 });/*
22254  * Based on:
22255  * Ext JS Library 1.1.1
22256  * Copyright(c) 2006-2007, Ext JS, LLC.
22257  *
22258  * Originally Released Under LGPL - original licence link has changed is not relivant.
22259  *
22260  * Fork - LGPL
22261  * <script type="text/javascript">
22262  */
22263
22264 /**
22265  * @class Roo.data.ScriptTagProxy
22266  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22267  * other than the originating domain of the running page.<br><br>
22268  * <p>
22269  * <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
22270  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22271  * <p>
22272  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22273  * source code that is used as the source inside a &lt;script> tag.<br><br>
22274  * <p>
22275  * In order for the browser to process the returned data, the server must wrap the data object
22276  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22277  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22278  * depending on whether the callback name was passed:
22279  * <p>
22280  * <pre><code>
22281 boolean scriptTag = false;
22282 String cb = request.getParameter("callback");
22283 if (cb != null) {
22284     scriptTag = true;
22285     response.setContentType("text/javascript");
22286 } else {
22287     response.setContentType("application/x-json");
22288 }
22289 Writer out = response.getWriter();
22290 if (scriptTag) {
22291     out.write(cb + "(");
22292 }
22293 out.print(dataBlock.toJsonString());
22294 if (scriptTag) {
22295     out.write(");");
22296 }
22297 </pre></code>
22298  *
22299  * @constructor
22300  * @param {Object} config A configuration object.
22301  */
22302 Roo.data.ScriptTagProxy = function(config){
22303     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22304     Roo.apply(this, config);
22305     this.head = document.getElementsByTagName("head")[0];
22306 };
22307
22308 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22309
22310 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22311     /**
22312      * @cfg {String} url The URL from which to request the data object.
22313      */
22314     /**
22315      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22316      */
22317     timeout : 30000,
22318     /**
22319      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22320      * the server the name of the callback function set up by the load call to process the returned data object.
22321      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22322      * javascript output which calls this named function passing the data object as its only parameter.
22323      */
22324     callbackParam : "callback",
22325     /**
22326      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22327      * name to the request.
22328      */
22329     nocache : true,
22330
22331     /**
22332      * Load data from the configured URL, read the data object into
22333      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22334      * process that block using the passed callback.
22335      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22336      * for the request to the remote server.
22337      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22338      * object into a block of Roo.data.Records.
22339      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22340      * The function must be passed <ul>
22341      * <li>The Record block object</li>
22342      * <li>The "arg" argument from the load function</li>
22343      * <li>A boolean success indicator</li>
22344      * </ul>
22345      * @param {Object} scope The scope in which to call the callback
22346      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22347      */
22348     load : function(params, reader, callback, scope, arg){
22349         if(this.fireEvent("beforeload", this, params) !== false){
22350
22351             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22352
22353             var url = this.url;
22354             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22355             if(this.nocache){
22356                 url += "&_dc=" + (new Date().getTime());
22357             }
22358             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22359             var trans = {
22360                 id : transId,
22361                 cb : "stcCallback"+transId,
22362                 scriptId : "stcScript"+transId,
22363                 params : params,
22364                 arg : arg,
22365                 url : url,
22366                 callback : callback,
22367                 scope : scope,
22368                 reader : reader
22369             };
22370             var conn = this;
22371
22372             window[trans.cb] = function(o){
22373                 conn.handleResponse(o, trans);
22374             };
22375
22376             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22377
22378             if(this.autoAbort !== false){
22379                 this.abort();
22380             }
22381
22382             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22383
22384             var script = document.createElement("script");
22385             script.setAttribute("src", url);
22386             script.setAttribute("type", "text/javascript");
22387             script.setAttribute("id", trans.scriptId);
22388             this.head.appendChild(script);
22389
22390             this.trans = trans;
22391         }else{
22392             callback.call(scope||this, null, arg, false);
22393         }
22394     },
22395
22396     // private
22397     isLoading : function(){
22398         return this.trans ? true : false;
22399     },
22400
22401     /**
22402      * Abort the current server request.
22403      */
22404     abort : function(){
22405         if(this.isLoading()){
22406             this.destroyTrans(this.trans);
22407         }
22408     },
22409
22410     // private
22411     destroyTrans : function(trans, isLoaded){
22412         this.head.removeChild(document.getElementById(trans.scriptId));
22413         clearTimeout(trans.timeoutId);
22414         if(isLoaded){
22415             window[trans.cb] = undefined;
22416             try{
22417                 delete window[trans.cb];
22418             }catch(e){}
22419         }else{
22420             // if hasn't been loaded, wait for load to remove it to prevent script error
22421             window[trans.cb] = function(){
22422                 window[trans.cb] = undefined;
22423                 try{
22424                     delete window[trans.cb];
22425                 }catch(e){}
22426             };
22427         }
22428     },
22429
22430     // private
22431     handleResponse : function(o, trans){
22432         this.trans = false;
22433         this.destroyTrans(trans, true);
22434         var result;
22435         try {
22436             result = trans.reader.readRecords(o);
22437         }catch(e){
22438             this.fireEvent("loadexception", this, o, trans.arg, e);
22439             trans.callback.call(trans.scope||window, null, trans.arg, false);
22440             return;
22441         }
22442         this.fireEvent("load", this, o, trans.arg);
22443         trans.callback.call(trans.scope||window, result, trans.arg, true);
22444     },
22445
22446     // private
22447     handleFailure : function(trans){
22448         this.trans = false;
22449         this.destroyTrans(trans, false);
22450         this.fireEvent("loadexception", this, null, trans.arg);
22451         trans.callback.call(trans.scope||window, null, trans.arg, false);
22452     }
22453 });/*
22454  * Based on:
22455  * Ext JS Library 1.1.1
22456  * Copyright(c) 2006-2007, Ext JS, LLC.
22457  *
22458  * Originally Released Under LGPL - original licence link has changed is not relivant.
22459  *
22460  * Fork - LGPL
22461  * <script type="text/javascript">
22462  */
22463
22464 /**
22465  * @class Roo.data.JsonReader
22466  * @extends Roo.data.DataReader
22467  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22468  * based on mappings in a provided Roo.data.Record constructor.
22469  * 
22470  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22471  * in the reply previously. 
22472  * 
22473  * <p>
22474  * Example code:
22475  * <pre><code>
22476 var RecordDef = Roo.data.Record.create([
22477     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22478     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22479 ]);
22480 var myReader = new Roo.data.JsonReader({
22481     totalProperty: "results",    // The property which contains the total dataset size (optional)
22482     root: "rows",                // The property which contains an Array of row objects
22483     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22484 }, RecordDef);
22485 </code></pre>
22486  * <p>
22487  * This would consume a JSON file like this:
22488  * <pre><code>
22489 { 'results': 2, 'rows': [
22490     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22491     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22492 }
22493 </code></pre>
22494  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22495  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22496  * paged from the remote server.
22497  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22498  * @cfg {String} root name of the property which contains the Array of row objects.
22499  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22500  * @constructor
22501  * Create a new JsonReader
22502  * @param {Object} meta Metadata configuration options
22503  * @param {Object} recordType Either an Array of field definition objects,
22504  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22505  */
22506 Roo.data.JsonReader = function(meta, recordType){
22507     
22508     meta = meta || {};
22509     // set some defaults:
22510     Roo.applyIf(meta, {
22511         totalProperty: 'total',
22512         successProperty : 'success',
22513         root : 'data',
22514         id : 'id'
22515     });
22516     
22517     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22518 };
22519 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22520     
22521     /**
22522      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22523      * Used by Store query builder to append _requestMeta to params.
22524      * 
22525      */
22526     metaFromRemote : false,
22527     /**
22528      * This method is only used by a DataProxy which has retrieved data from a remote server.
22529      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22530      * @return {Object} data A data block which is used by an Roo.data.Store object as
22531      * a cache of Roo.data.Records.
22532      */
22533     read : function(response){
22534         var json = response.responseText;
22535        
22536         var o = /* eval:var:o */ eval("("+json+")");
22537         if(!o) {
22538             throw {message: "JsonReader.read: Json object not found"};
22539         }
22540         
22541         if(o.metaData){
22542             
22543             delete this.ef;
22544             this.metaFromRemote = true;
22545             this.meta = o.metaData;
22546             this.recordType = Roo.data.Record.create(o.metaData.fields);
22547             this.onMetaChange(this.meta, this.recordType, o);
22548         }
22549         return this.readRecords(o);
22550     },
22551
22552     // private function a store will implement
22553     onMetaChange : function(meta, recordType, o){
22554
22555     },
22556
22557     /**
22558          * @ignore
22559          */
22560     simpleAccess: function(obj, subsc) {
22561         return obj[subsc];
22562     },
22563
22564         /**
22565          * @ignore
22566          */
22567     getJsonAccessor: function(){
22568         var re = /[\[\.]/;
22569         return function(expr) {
22570             try {
22571                 return(re.test(expr))
22572                     ? new Function("obj", "return obj." + expr)
22573                     : function(obj){
22574                         return obj[expr];
22575                     };
22576             } catch(e){}
22577             return Roo.emptyFn;
22578         };
22579     }(),
22580
22581     /**
22582      * Create a data block containing Roo.data.Records from an XML document.
22583      * @param {Object} o An object which contains an Array of row objects in the property specified
22584      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22585      * which contains the total size of the dataset.
22586      * @return {Object} data A data block which is used by an Roo.data.Store object as
22587      * a cache of Roo.data.Records.
22588      */
22589     readRecords : function(o){
22590         /**
22591          * After any data loads, the raw JSON data is available for further custom processing.
22592          * @type Object
22593          */
22594         this.o = o;
22595         var s = this.meta, Record = this.recordType,
22596             f = Record.prototype.fields, fi = f.items, fl = f.length;
22597
22598 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22599         if (!this.ef) {
22600             if(s.totalProperty) {
22601                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22602                 }
22603                 if(s.successProperty) {
22604                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22605                 }
22606                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22607                 if (s.id) {
22608                         var g = this.getJsonAccessor(s.id);
22609                         this.getId = function(rec) {
22610                                 var r = g(rec);
22611                                 return (r === undefined || r === "") ? null : r;
22612                         };
22613                 } else {
22614                         this.getId = function(){return null;};
22615                 }
22616             this.ef = [];
22617             for(var jj = 0; jj < fl; jj++){
22618                 f = fi[jj];
22619                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22620                 this.ef[jj] = this.getJsonAccessor(map);
22621             }
22622         }
22623
22624         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22625         if(s.totalProperty){
22626             var vt = parseInt(this.getTotal(o), 10);
22627             if(!isNaN(vt)){
22628                 totalRecords = vt;
22629             }
22630         }
22631         if(s.successProperty){
22632             var vs = this.getSuccess(o);
22633             if(vs === false || vs === 'false'){
22634                 success = false;
22635             }
22636         }
22637         var records = [];
22638             for(var i = 0; i < c; i++){
22639                     var n = root[i];
22640                 var values = {};
22641                 var id = this.getId(n);
22642                 for(var j = 0; j < fl; j++){
22643                     f = fi[j];
22644                 var v = this.ef[j](n);
22645                 if (!f.convert) {
22646                     Roo.log('missing convert for ' + f.name);
22647                     Roo.log(f);
22648                     continue;
22649                 }
22650                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22651                 }
22652                 var record = new Record(values, id);
22653                 record.json = n;
22654                 records[i] = record;
22655             }
22656             return {
22657             raw : o,
22658                 success : success,
22659                 records : records,
22660                 totalRecords : totalRecords
22661             };
22662     }
22663 });/*
22664  * Based on:
22665  * Ext JS Library 1.1.1
22666  * Copyright(c) 2006-2007, Ext JS, LLC.
22667  *
22668  * Originally Released Under LGPL - original licence link has changed is not relivant.
22669  *
22670  * Fork - LGPL
22671  * <script type="text/javascript">
22672  */
22673
22674 /**
22675  * @class Roo.data.XmlReader
22676  * @extends Roo.data.DataReader
22677  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22678  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22679  * <p>
22680  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22681  * header in the HTTP response must be set to "text/xml".</em>
22682  * <p>
22683  * Example code:
22684  * <pre><code>
22685 var RecordDef = Roo.data.Record.create([
22686    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22687    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22688 ]);
22689 var myReader = new Roo.data.XmlReader({
22690    totalRecords: "results", // The element which contains the total dataset size (optional)
22691    record: "row",           // The repeated element which contains row information
22692    id: "id"                 // The element within the row that provides an ID for the record (optional)
22693 }, RecordDef);
22694 </code></pre>
22695  * <p>
22696  * This would consume an XML file like this:
22697  * <pre><code>
22698 &lt;?xml?>
22699 &lt;dataset>
22700  &lt;results>2&lt;/results>
22701  &lt;row>
22702    &lt;id>1&lt;/id>
22703    &lt;name>Bill&lt;/name>
22704    &lt;occupation>Gardener&lt;/occupation>
22705  &lt;/row>
22706  &lt;row>
22707    &lt;id>2&lt;/id>
22708    &lt;name>Ben&lt;/name>
22709    &lt;occupation>Horticulturalist&lt;/occupation>
22710  &lt;/row>
22711 &lt;/dataset>
22712 </code></pre>
22713  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22714  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22715  * paged from the remote server.
22716  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22717  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22718  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22719  * a record identifier value.
22720  * @constructor
22721  * Create a new XmlReader
22722  * @param {Object} meta Metadata configuration options
22723  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22724  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22725  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22726  */
22727 Roo.data.XmlReader = function(meta, recordType){
22728     meta = meta || {};
22729     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22730 };
22731 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22732     /**
22733      * This method is only used by a DataProxy which has retrieved data from a remote server.
22734          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22735          * to contain a method called 'responseXML' that returns an XML document object.
22736      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22737      * a cache of Roo.data.Records.
22738      */
22739     read : function(response){
22740         var doc = response.responseXML;
22741         if(!doc) {
22742             throw {message: "XmlReader.read: XML Document not available"};
22743         }
22744         return this.readRecords(doc);
22745     },
22746
22747     /**
22748      * Create a data block containing Roo.data.Records from an XML document.
22749          * @param {Object} doc A parsed XML document.
22750      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22751      * a cache of Roo.data.Records.
22752      */
22753     readRecords : function(doc){
22754         /**
22755          * After any data loads/reads, the raw XML Document is available for further custom processing.
22756          * @type XMLDocument
22757          */
22758         this.xmlData = doc;
22759         var root = doc.documentElement || doc;
22760         var q = Roo.DomQuery;
22761         var recordType = this.recordType, fields = recordType.prototype.fields;
22762         var sid = this.meta.id;
22763         var totalRecords = 0, success = true;
22764         if(this.meta.totalRecords){
22765             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22766         }
22767         
22768         if(this.meta.success){
22769             var sv = q.selectValue(this.meta.success, root, true);
22770             success = sv !== false && sv !== 'false';
22771         }
22772         var records = [];
22773         var ns = q.select(this.meta.record, root);
22774         for(var i = 0, len = ns.length; i < len; i++) {
22775                 var n = ns[i];
22776                 var values = {};
22777                 var id = sid ? q.selectValue(sid, n) : undefined;
22778                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22779                     var f = fields.items[j];
22780                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22781                     v = f.convert(v);
22782                     values[f.name] = v;
22783                 }
22784                 var record = new recordType(values, id);
22785                 record.node = n;
22786                 records[records.length] = record;
22787             }
22788
22789             return {
22790                 success : success,
22791                 records : records,
22792                 totalRecords : totalRecords || records.length
22793             };
22794     }
22795 });/*
22796  * Based on:
22797  * Ext JS Library 1.1.1
22798  * Copyright(c) 2006-2007, Ext JS, LLC.
22799  *
22800  * Originally Released Under LGPL - original licence link has changed is not relivant.
22801  *
22802  * Fork - LGPL
22803  * <script type="text/javascript">
22804  */
22805
22806 /**
22807  * @class Roo.data.ArrayReader
22808  * @extends Roo.data.DataReader
22809  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22810  * Each element of that Array represents a row of data fields. The
22811  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22812  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22813  * <p>
22814  * Example code:.
22815  * <pre><code>
22816 var RecordDef = Roo.data.Record.create([
22817     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22818     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22819 ]);
22820 var myReader = new Roo.data.ArrayReader({
22821     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22822 }, RecordDef);
22823 </code></pre>
22824  * <p>
22825  * This would consume an Array like this:
22826  * <pre><code>
22827 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22828   </code></pre>
22829  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22830  * @constructor
22831  * Create a new JsonReader
22832  * @param {Object} meta Metadata configuration options.
22833  * @param {Object} recordType Either an Array of field definition objects
22834  * as specified to {@link Roo.data.Record#create},
22835  * or an {@link Roo.data.Record} object
22836  * created using {@link Roo.data.Record#create}.
22837  */
22838 Roo.data.ArrayReader = function(meta, recordType){
22839     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22840 };
22841
22842 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22843     /**
22844      * Create a data block containing Roo.data.Records from an XML document.
22845      * @param {Object} o An Array of row objects which represents the dataset.
22846      * @return {Object} data A data block which is used by an Roo.data.Store object as
22847      * a cache of Roo.data.Records.
22848      */
22849     readRecords : function(o){
22850         var sid = this.meta ? this.meta.id : null;
22851         var recordType = this.recordType, fields = recordType.prototype.fields;
22852         var records = [];
22853         var root = o;
22854             for(var i = 0; i < root.length; i++){
22855                     var n = root[i];
22856                 var values = {};
22857                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22858                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22859                 var f = fields.items[j];
22860                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22861                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22862                 v = f.convert(v);
22863                 values[f.name] = v;
22864             }
22865                 var record = new recordType(values, id);
22866                 record.json = n;
22867                 records[records.length] = record;
22868             }
22869             return {
22870                 records : records,
22871                 totalRecords : records.length
22872             };
22873     }
22874 });/*
22875  * Based on:
22876  * Ext JS Library 1.1.1
22877  * Copyright(c) 2006-2007, Ext JS, LLC.
22878  *
22879  * Originally Released Under LGPL - original licence link has changed is not relivant.
22880  *
22881  * Fork - LGPL
22882  * <script type="text/javascript">
22883  */
22884
22885
22886 /**
22887  * @class Roo.data.Tree
22888  * @extends Roo.util.Observable
22889  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22890  * in the tree have most standard DOM functionality.
22891  * @constructor
22892  * @param {Node} root (optional) The root node
22893  */
22894 Roo.data.Tree = function(root){
22895    this.nodeHash = {};
22896    /**
22897     * The root node for this tree
22898     * @type Node
22899     */
22900    this.root = null;
22901    if(root){
22902        this.setRootNode(root);
22903    }
22904    this.addEvents({
22905        /**
22906         * @event append
22907         * Fires when a new child node is appended to a node in this tree.
22908         * @param {Tree} tree The owner tree
22909         * @param {Node} parent The parent node
22910         * @param {Node} node The newly appended node
22911         * @param {Number} index The index of the newly appended node
22912         */
22913        "append" : true,
22914        /**
22915         * @event remove
22916         * Fires when a child node is removed from a node in this tree.
22917         * @param {Tree} tree The owner tree
22918         * @param {Node} parent The parent node
22919         * @param {Node} node The child node removed
22920         */
22921        "remove" : true,
22922        /**
22923         * @event move
22924         * Fires when a node is moved to a new location in the tree
22925         * @param {Tree} tree The owner tree
22926         * @param {Node} node The node moved
22927         * @param {Node} oldParent The old parent of this node
22928         * @param {Node} newParent The new parent of this node
22929         * @param {Number} index The index it was moved to
22930         */
22931        "move" : true,
22932        /**
22933         * @event insert
22934         * Fires when a new child node is inserted in a node in this tree.
22935         * @param {Tree} tree The owner tree
22936         * @param {Node} parent The parent node
22937         * @param {Node} node The child node inserted
22938         * @param {Node} refNode The child node the node was inserted before
22939         */
22940        "insert" : true,
22941        /**
22942         * @event beforeappend
22943         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22944         * @param {Tree} tree The owner tree
22945         * @param {Node} parent The parent node
22946         * @param {Node} node The child node to be appended
22947         */
22948        "beforeappend" : true,
22949        /**
22950         * @event beforeremove
22951         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22952         * @param {Tree} tree The owner tree
22953         * @param {Node} parent The parent node
22954         * @param {Node} node The child node to be removed
22955         */
22956        "beforeremove" : true,
22957        /**
22958         * @event beforemove
22959         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22960         * @param {Tree} tree The owner tree
22961         * @param {Node} node The node being moved
22962         * @param {Node} oldParent The parent of the node
22963         * @param {Node} newParent The new parent the node is moving to
22964         * @param {Number} index The index it is being moved to
22965         */
22966        "beforemove" : true,
22967        /**
22968         * @event beforeinsert
22969         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22970         * @param {Tree} tree The owner tree
22971         * @param {Node} parent The parent node
22972         * @param {Node} node The child node to be inserted
22973         * @param {Node} refNode The child node the node is being inserted before
22974         */
22975        "beforeinsert" : true
22976    });
22977
22978     Roo.data.Tree.superclass.constructor.call(this);
22979 };
22980
22981 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22982     pathSeparator: "/",
22983
22984     proxyNodeEvent : function(){
22985         return this.fireEvent.apply(this, arguments);
22986     },
22987
22988     /**
22989      * Returns the root node for this tree.
22990      * @return {Node}
22991      */
22992     getRootNode : function(){
22993         return this.root;
22994     },
22995
22996     /**
22997      * Sets the root node for this tree.
22998      * @param {Node} node
22999      * @return {Node}
23000      */
23001     setRootNode : function(node){
23002         this.root = node;
23003         node.ownerTree = this;
23004         node.isRoot = true;
23005         this.registerNode(node);
23006         return node;
23007     },
23008
23009     /**
23010      * Gets a node in this tree by its id.
23011      * @param {String} id
23012      * @return {Node}
23013      */
23014     getNodeById : function(id){
23015         return this.nodeHash[id];
23016     },
23017
23018     registerNode : function(node){
23019         this.nodeHash[node.id] = node;
23020     },
23021
23022     unregisterNode : function(node){
23023         delete this.nodeHash[node.id];
23024     },
23025
23026     toString : function(){
23027         return "[Tree"+(this.id?" "+this.id:"")+"]";
23028     }
23029 });
23030
23031 /**
23032  * @class Roo.data.Node
23033  * @extends Roo.util.Observable
23034  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23035  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23036  * @constructor
23037  * @param {Object} attributes The attributes/config for the node
23038  */
23039 Roo.data.Node = function(attributes){
23040     /**
23041      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23042      * @type {Object}
23043      */
23044     this.attributes = attributes || {};
23045     this.leaf = this.attributes.leaf;
23046     /**
23047      * The node id. @type String
23048      */
23049     this.id = this.attributes.id;
23050     if(!this.id){
23051         this.id = Roo.id(null, "ynode-");
23052         this.attributes.id = this.id;
23053     }
23054      
23055     
23056     /**
23057      * All child nodes of this node. @type Array
23058      */
23059     this.childNodes = [];
23060     if(!this.childNodes.indexOf){ // indexOf is a must
23061         this.childNodes.indexOf = function(o){
23062             for(var i = 0, len = this.length; i < len; i++){
23063                 if(this[i] == o) {
23064                     return i;
23065                 }
23066             }
23067             return -1;
23068         };
23069     }
23070     /**
23071      * The parent node for this node. @type Node
23072      */
23073     this.parentNode = null;
23074     /**
23075      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23076      */
23077     this.firstChild = null;
23078     /**
23079      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23080      */
23081     this.lastChild = null;
23082     /**
23083      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23084      */
23085     this.previousSibling = null;
23086     /**
23087      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23088      */
23089     this.nextSibling = null;
23090
23091     this.addEvents({
23092        /**
23093         * @event append
23094         * Fires when a new child node is appended
23095         * @param {Tree} tree The owner tree
23096         * @param {Node} this This node
23097         * @param {Node} node The newly appended node
23098         * @param {Number} index The index of the newly appended node
23099         */
23100        "append" : true,
23101        /**
23102         * @event remove
23103         * Fires when a child node is removed
23104         * @param {Tree} tree The owner tree
23105         * @param {Node} this This node
23106         * @param {Node} node The removed node
23107         */
23108        "remove" : true,
23109        /**
23110         * @event move
23111         * Fires when this node is moved to a new location in the tree
23112         * @param {Tree} tree The owner tree
23113         * @param {Node} this This node
23114         * @param {Node} oldParent The old parent of this node
23115         * @param {Node} newParent The new parent of this node
23116         * @param {Number} index The index it was moved to
23117         */
23118        "move" : true,
23119        /**
23120         * @event insert
23121         * Fires when a new child node is inserted.
23122         * @param {Tree} tree The owner tree
23123         * @param {Node} this This node
23124         * @param {Node} node The child node inserted
23125         * @param {Node} refNode The child node the node was inserted before
23126         */
23127        "insert" : true,
23128        /**
23129         * @event beforeappend
23130         * Fires before a new child is appended, return false to cancel the append.
23131         * @param {Tree} tree The owner tree
23132         * @param {Node} this This node
23133         * @param {Node} node The child node to be appended
23134         */
23135        "beforeappend" : true,
23136        /**
23137         * @event beforeremove
23138         * Fires before a child is removed, return false to cancel the remove.
23139         * @param {Tree} tree The owner tree
23140         * @param {Node} this This node
23141         * @param {Node} node The child node to be removed
23142         */
23143        "beforeremove" : true,
23144        /**
23145         * @event beforemove
23146         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23147         * @param {Tree} tree The owner tree
23148         * @param {Node} this This node
23149         * @param {Node} oldParent The parent of this node
23150         * @param {Node} newParent The new parent this node is moving to
23151         * @param {Number} index The index it is being moved to
23152         */
23153        "beforemove" : true,
23154        /**
23155         * @event beforeinsert
23156         * Fires before a new child is inserted, return false to cancel the insert.
23157         * @param {Tree} tree The owner tree
23158         * @param {Node} this This node
23159         * @param {Node} node The child node to be inserted
23160         * @param {Node} refNode The child node the node is being inserted before
23161         */
23162        "beforeinsert" : true
23163    });
23164     this.listeners = this.attributes.listeners;
23165     Roo.data.Node.superclass.constructor.call(this);
23166 };
23167
23168 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23169     fireEvent : function(evtName){
23170         // first do standard event for this node
23171         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23172             return false;
23173         }
23174         // then bubble it up to the tree if the event wasn't cancelled
23175         var ot = this.getOwnerTree();
23176         if(ot){
23177             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23178                 return false;
23179             }
23180         }
23181         return true;
23182     },
23183
23184     /**
23185      * Returns true if this node is a leaf
23186      * @return {Boolean}
23187      */
23188     isLeaf : function(){
23189         return this.leaf === true;
23190     },
23191
23192     // private
23193     setFirstChild : function(node){
23194         this.firstChild = node;
23195     },
23196
23197     //private
23198     setLastChild : function(node){
23199         this.lastChild = node;
23200     },
23201
23202
23203     /**
23204      * Returns true if this node is the last child of its parent
23205      * @return {Boolean}
23206      */
23207     isLast : function(){
23208        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23209     },
23210
23211     /**
23212      * Returns true if this node is the first child of its parent
23213      * @return {Boolean}
23214      */
23215     isFirst : function(){
23216        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23217     },
23218
23219     hasChildNodes : function(){
23220         return !this.isLeaf() && this.childNodes.length > 0;
23221     },
23222
23223     /**
23224      * Insert node(s) as the last child node of this node.
23225      * @param {Node/Array} node The node or Array of nodes to append
23226      * @return {Node} The appended node if single append, or null if an array was passed
23227      */
23228     appendChild : function(node){
23229         var multi = false;
23230         if(node instanceof Array){
23231             multi = node;
23232         }else if(arguments.length > 1){
23233             multi = arguments;
23234         }
23235         // if passed an array or multiple args do them one by one
23236         if(multi){
23237             for(var i = 0, len = multi.length; i < len; i++) {
23238                 this.appendChild(multi[i]);
23239             }
23240         }else{
23241             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23242                 return false;
23243             }
23244             var index = this.childNodes.length;
23245             var oldParent = node.parentNode;
23246             // it's a move, make sure we move it cleanly
23247             if(oldParent){
23248                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23249                     return false;
23250                 }
23251                 oldParent.removeChild(node);
23252             }
23253             index = this.childNodes.length;
23254             if(index == 0){
23255                 this.setFirstChild(node);
23256             }
23257             this.childNodes.push(node);
23258             node.parentNode = this;
23259             var ps = this.childNodes[index-1];
23260             if(ps){
23261                 node.previousSibling = ps;
23262                 ps.nextSibling = node;
23263             }else{
23264                 node.previousSibling = null;
23265             }
23266             node.nextSibling = null;
23267             this.setLastChild(node);
23268             node.setOwnerTree(this.getOwnerTree());
23269             this.fireEvent("append", this.ownerTree, this, node, index);
23270             if(oldParent){
23271                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23272             }
23273             return node;
23274         }
23275     },
23276
23277     /**
23278      * Removes a child node from this node.
23279      * @param {Node} node The node to remove
23280      * @return {Node} The removed node
23281      */
23282     removeChild : function(node){
23283         var index = this.childNodes.indexOf(node);
23284         if(index == -1){
23285             return false;
23286         }
23287         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23288             return false;
23289         }
23290
23291         // remove it from childNodes collection
23292         this.childNodes.splice(index, 1);
23293
23294         // update siblings
23295         if(node.previousSibling){
23296             node.previousSibling.nextSibling = node.nextSibling;
23297         }
23298         if(node.nextSibling){
23299             node.nextSibling.previousSibling = node.previousSibling;
23300         }
23301
23302         // update child refs
23303         if(this.firstChild == node){
23304             this.setFirstChild(node.nextSibling);
23305         }
23306         if(this.lastChild == node){
23307             this.setLastChild(node.previousSibling);
23308         }
23309
23310         node.setOwnerTree(null);
23311         // clear any references from the node
23312         node.parentNode = null;
23313         node.previousSibling = null;
23314         node.nextSibling = null;
23315         this.fireEvent("remove", this.ownerTree, this, node);
23316         return node;
23317     },
23318
23319     /**
23320      * Inserts the first node before the second node in this nodes childNodes collection.
23321      * @param {Node} node The node to insert
23322      * @param {Node} refNode The node to insert before (if null the node is appended)
23323      * @return {Node} The inserted node
23324      */
23325     insertBefore : function(node, refNode){
23326         if(!refNode){ // like standard Dom, refNode can be null for append
23327             return this.appendChild(node);
23328         }
23329         // nothing to do
23330         if(node == refNode){
23331             return false;
23332         }
23333
23334         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23335             return false;
23336         }
23337         var index = this.childNodes.indexOf(refNode);
23338         var oldParent = node.parentNode;
23339         var refIndex = index;
23340
23341         // when moving internally, indexes will change after remove
23342         if(oldParent == this && this.childNodes.indexOf(node) < index){
23343             refIndex--;
23344         }
23345
23346         // it's a move, make sure we move it cleanly
23347         if(oldParent){
23348             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23349                 return false;
23350             }
23351             oldParent.removeChild(node);
23352         }
23353         if(refIndex == 0){
23354             this.setFirstChild(node);
23355         }
23356         this.childNodes.splice(refIndex, 0, node);
23357         node.parentNode = this;
23358         var ps = this.childNodes[refIndex-1];
23359         if(ps){
23360             node.previousSibling = ps;
23361             ps.nextSibling = node;
23362         }else{
23363             node.previousSibling = null;
23364         }
23365         node.nextSibling = refNode;
23366         refNode.previousSibling = node;
23367         node.setOwnerTree(this.getOwnerTree());
23368         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23369         if(oldParent){
23370             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23371         }
23372         return node;
23373     },
23374
23375     /**
23376      * Returns the child node at the specified index.
23377      * @param {Number} index
23378      * @return {Node}
23379      */
23380     item : function(index){
23381         return this.childNodes[index];
23382     },
23383
23384     /**
23385      * Replaces one child node in this node with another.
23386      * @param {Node} newChild The replacement node
23387      * @param {Node} oldChild The node to replace
23388      * @return {Node} The replaced node
23389      */
23390     replaceChild : function(newChild, oldChild){
23391         this.insertBefore(newChild, oldChild);
23392         this.removeChild(oldChild);
23393         return oldChild;
23394     },
23395
23396     /**
23397      * Returns the index of a child node
23398      * @param {Node} node
23399      * @return {Number} The index of the node or -1 if it was not found
23400      */
23401     indexOf : function(child){
23402         return this.childNodes.indexOf(child);
23403     },
23404
23405     /**
23406      * Returns the tree this node is in.
23407      * @return {Tree}
23408      */
23409     getOwnerTree : function(){
23410         // if it doesn't have one, look for one
23411         if(!this.ownerTree){
23412             var p = this;
23413             while(p){
23414                 if(p.ownerTree){
23415                     this.ownerTree = p.ownerTree;
23416                     break;
23417                 }
23418                 p = p.parentNode;
23419             }
23420         }
23421         return this.ownerTree;
23422     },
23423
23424     /**
23425      * Returns depth of this node (the root node has a depth of 0)
23426      * @return {Number}
23427      */
23428     getDepth : function(){
23429         var depth = 0;
23430         var p = this;
23431         while(p.parentNode){
23432             ++depth;
23433             p = p.parentNode;
23434         }
23435         return depth;
23436     },
23437
23438     // private
23439     setOwnerTree : function(tree){
23440         // if it's move, we need to update everyone
23441         if(tree != this.ownerTree){
23442             if(this.ownerTree){
23443                 this.ownerTree.unregisterNode(this);
23444             }
23445             this.ownerTree = tree;
23446             var cs = this.childNodes;
23447             for(var i = 0, len = cs.length; i < len; i++) {
23448                 cs[i].setOwnerTree(tree);
23449             }
23450             if(tree){
23451                 tree.registerNode(this);
23452             }
23453         }
23454     },
23455
23456     /**
23457      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23458      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23459      * @return {String} The path
23460      */
23461     getPath : function(attr){
23462         attr = attr || "id";
23463         var p = this.parentNode;
23464         var b = [this.attributes[attr]];
23465         while(p){
23466             b.unshift(p.attributes[attr]);
23467             p = p.parentNode;
23468         }
23469         var sep = this.getOwnerTree().pathSeparator;
23470         return sep + b.join(sep);
23471     },
23472
23473     /**
23474      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23475      * function call will be the scope provided or the current node. The arguments to the function
23476      * will be the args provided or the current node. If the function returns false at any point,
23477      * the bubble is stopped.
23478      * @param {Function} fn The function to call
23479      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23480      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23481      */
23482     bubble : function(fn, scope, args){
23483         var p = this;
23484         while(p){
23485             if(fn.call(scope || p, args || p) === false){
23486                 break;
23487             }
23488             p = p.parentNode;
23489         }
23490     },
23491
23492     /**
23493      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23494      * function call will be the scope provided or the current node. The arguments to the function
23495      * will be the args provided or the current node. If the function returns false at any point,
23496      * the cascade is stopped on that branch.
23497      * @param {Function} fn The function to call
23498      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23499      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23500      */
23501     cascade : function(fn, scope, args){
23502         if(fn.call(scope || this, args || this) !== false){
23503             var cs = this.childNodes;
23504             for(var i = 0, len = cs.length; i < len; i++) {
23505                 cs[i].cascade(fn, scope, args);
23506             }
23507         }
23508     },
23509
23510     /**
23511      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23512      * function call will be the scope provided or the current node. The arguments to the function
23513      * will be the args provided or the current node. If the function returns false at any point,
23514      * the iteration stops.
23515      * @param {Function} fn The function to call
23516      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23517      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23518      */
23519     eachChild : function(fn, scope, args){
23520         var cs = this.childNodes;
23521         for(var i = 0, len = cs.length; i < len; i++) {
23522                 if(fn.call(scope || this, args || cs[i]) === false){
23523                     break;
23524                 }
23525         }
23526     },
23527
23528     /**
23529      * Finds the first child that has the attribute with the specified value.
23530      * @param {String} attribute The attribute name
23531      * @param {Mixed} value The value to search for
23532      * @return {Node} The found child or null if none was found
23533      */
23534     findChild : function(attribute, value){
23535         var cs = this.childNodes;
23536         for(var i = 0, len = cs.length; i < len; i++) {
23537                 if(cs[i].attributes[attribute] == value){
23538                     return cs[i];
23539                 }
23540         }
23541         return null;
23542     },
23543
23544     /**
23545      * Finds the first child by a custom function. The child matches if the function passed
23546      * returns true.
23547      * @param {Function} fn
23548      * @param {Object} scope (optional)
23549      * @return {Node} The found child or null if none was found
23550      */
23551     findChildBy : function(fn, scope){
23552         var cs = this.childNodes;
23553         for(var i = 0, len = cs.length; i < len; i++) {
23554                 if(fn.call(scope||cs[i], cs[i]) === true){
23555                     return cs[i];
23556                 }
23557         }
23558         return null;
23559     },
23560
23561     /**
23562      * Sorts this nodes children using the supplied sort function
23563      * @param {Function} fn
23564      * @param {Object} scope (optional)
23565      */
23566     sort : function(fn, scope){
23567         var cs = this.childNodes;
23568         var len = cs.length;
23569         if(len > 0){
23570             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23571             cs.sort(sortFn);
23572             for(var i = 0; i < len; i++){
23573                 var n = cs[i];
23574                 n.previousSibling = cs[i-1];
23575                 n.nextSibling = cs[i+1];
23576                 if(i == 0){
23577                     this.setFirstChild(n);
23578                 }
23579                 if(i == len-1){
23580                     this.setLastChild(n);
23581                 }
23582             }
23583         }
23584     },
23585
23586     /**
23587      * Returns true if this node is an ancestor (at any point) of the passed node.
23588      * @param {Node} node
23589      * @return {Boolean}
23590      */
23591     contains : function(node){
23592         return node.isAncestor(this);
23593     },
23594
23595     /**
23596      * Returns true if the passed node is an ancestor (at any point) of this node.
23597      * @param {Node} node
23598      * @return {Boolean}
23599      */
23600     isAncestor : function(node){
23601         var p = this.parentNode;
23602         while(p){
23603             if(p == node){
23604                 return true;
23605             }
23606             p = p.parentNode;
23607         }
23608         return false;
23609     },
23610
23611     toString : function(){
23612         return "[Node"+(this.id?" "+this.id:"")+"]";
23613     }
23614 });/*
23615  * Based on:
23616  * Ext JS Library 1.1.1
23617  * Copyright(c) 2006-2007, Ext JS, LLC.
23618  *
23619  * Originally Released Under LGPL - original licence link has changed is not relivant.
23620  *
23621  * Fork - LGPL
23622  * <script type="text/javascript">
23623  */
23624  (function(){ 
23625 /**
23626  * @class Roo.Layer
23627  * @extends Roo.Element
23628  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23629  * automatic maintaining of shadow/shim positions.
23630  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23631  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23632  * you can pass a string with a CSS class name. False turns off the shadow.
23633  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23634  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23635  * @cfg {String} cls CSS class to add to the element
23636  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23637  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23638  * @constructor
23639  * @param {Object} config An object with config options.
23640  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23641  */
23642
23643 Roo.Layer = function(config, existingEl){
23644     config = config || {};
23645     var dh = Roo.DomHelper;
23646     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23647     if(existingEl){
23648         this.dom = Roo.getDom(existingEl);
23649     }
23650     if(!this.dom){
23651         var o = config.dh || {tag: "div", cls: "x-layer"};
23652         this.dom = dh.append(pel, o);
23653     }
23654     if(config.cls){
23655         this.addClass(config.cls);
23656     }
23657     this.constrain = config.constrain !== false;
23658     this.visibilityMode = Roo.Element.VISIBILITY;
23659     if(config.id){
23660         this.id = this.dom.id = config.id;
23661     }else{
23662         this.id = Roo.id(this.dom);
23663     }
23664     this.zindex = config.zindex || this.getZIndex();
23665     this.position("absolute", this.zindex);
23666     if(config.shadow){
23667         this.shadowOffset = config.shadowOffset || 4;
23668         this.shadow = new Roo.Shadow({
23669             offset : this.shadowOffset,
23670             mode : config.shadow
23671         });
23672     }else{
23673         this.shadowOffset = 0;
23674     }
23675     this.useShim = config.shim !== false && Roo.useShims;
23676     this.useDisplay = config.useDisplay;
23677     this.hide();
23678 };
23679
23680 var supr = Roo.Element.prototype;
23681
23682 // shims are shared among layer to keep from having 100 iframes
23683 var shims = [];
23684
23685 Roo.extend(Roo.Layer, Roo.Element, {
23686
23687     getZIndex : function(){
23688         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23689     },
23690
23691     getShim : function(){
23692         if(!this.useShim){
23693             return null;
23694         }
23695         if(this.shim){
23696             return this.shim;
23697         }
23698         var shim = shims.shift();
23699         if(!shim){
23700             shim = this.createShim();
23701             shim.enableDisplayMode('block');
23702             shim.dom.style.display = 'none';
23703             shim.dom.style.visibility = 'visible';
23704         }
23705         var pn = this.dom.parentNode;
23706         if(shim.dom.parentNode != pn){
23707             pn.insertBefore(shim.dom, this.dom);
23708         }
23709         shim.setStyle('z-index', this.getZIndex()-2);
23710         this.shim = shim;
23711         return shim;
23712     },
23713
23714     hideShim : function(){
23715         if(this.shim){
23716             this.shim.setDisplayed(false);
23717             shims.push(this.shim);
23718             delete this.shim;
23719         }
23720     },
23721
23722     disableShadow : function(){
23723         if(this.shadow){
23724             this.shadowDisabled = true;
23725             this.shadow.hide();
23726             this.lastShadowOffset = this.shadowOffset;
23727             this.shadowOffset = 0;
23728         }
23729     },
23730
23731     enableShadow : function(show){
23732         if(this.shadow){
23733             this.shadowDisabled = false;
23734             this.shadowOffset = this.lastShadowOffset;
23735             delete this.lastShadowOffset;
23736             if(show){
23737                 this.sync(true);
23738             }
23739         }
23740     },
23741
23742     // private
23743     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23744     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23745     sync : function(doShow){
23746         var sw = this.shadow;
23747         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23748             var sh = this.getShim();
23749
23750             var w = this.getWidth(),
23751                 h = this.getHeight();
23752
23753             var l = this.getLeft(true),
23754                 t = this.getTop(true);
23755
23756             if(sw && !this.shadowDisabled){
23757                 if(doShow && !sw.isVisible()){
23758                     sw.show(this);
23759                 }else{
23760                     sw.realign(l, t, w, h);
23761                 }
23762                 if(sh){
23763                     if(doShow){
23764                        sh.show();
23765                     }
23766                     // fit the shim behind the shadow, so it is shimmed too
23767                     var a = sw.adjusts, s = sh.dom.style;
23768                     s.left = (Math.min(l, l+a.l))+"px";
23769                     s.top = (Math.min(t, t+a.t))+"px";
23770                     s.width = (w+a.w)+"px";
23771                     s.height = (h+a.h)+"px";
23772                 }
23773             }else if(sh){
23774                 if(doShow){
23775                    sh.show();
23776                 }
23777                 sh.setSize(w, h);
23778                 sh.setLeftTop(l, t);
23779             }
23780             
23781         }
23782     },
23783
23784     // private
23785     destroy : function(){
23786         this.hideShim();
23787         if(this.shadow){
23788             this.shadow.hide();
23789         }
23790         this.removeAllListeners();
23791         var pn = this.dom.parentNode;
23792         if(pn){
23793             pn.removeChild(this.dom);
23794         }
23795         Roo.Element.uncache(this.id);
23796     },
23797
23798     remove : function(){
23799         this.destroy();
23800     },
23801
23802     // private
23803     beginUpdate : function(){
23804         this.updating = true;
23805     },
23806
23807     // private
23808     endUpdate : function(){
23809         this.updating = false;
23810         this.sync(true);
23811     },
23812
23813     // private
23814     hideUnders : function(negOffset){
23815         if(this.shadow){
23816             this.shadow.hide();
23817         }
23818         this.hideShim();
23819     },
23820
23821     // private
23822     constrainXY : function(){
23823         if(this.constrain){
23824             var vw = Roo.lib.Dom.getViewWidth(),
23825                 vh = Roo.lib.Dom.getViewHeight();
23826             var s = Roo.get(document).getScroll();
23827
23828             var xy = this.getXY();
23829             var x = xy[0], y = xy[1];   
23830             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23831             // only move it if it needs it
23832             var moved = false;
23833             // first validate right/bottom
23834             if((x + w) > vw+s.left){
23835                 x = vw - w - this.shadowOffset;
23836                 moved = true;
23837             }
23838             if((y + h) > vh+s.top){
23839                 y = vh - h - this.shadowOffset;
23840                 moved = true;
23841             }
23842             // then make sure top/left isn't negative
23843             if(x < s.left){
23844                 x = s.left;
23845                 moved = true;
23846             }
23847             if(y < s.top){
23848                 y = s.top;
23849                 moved = true;
23850             }
23851             if(moved){
23852                 if(this.avoidY){
23853                     var ay = this.avoidY;
23854                     if(y <= ay && (y+h) >= ay){
23855                         y = ay-h-5;   
23856                     }
23857                 }
23858                 xy = [x, y];
23859                 this.storeXY(xy);
23860                 supr.setXY.call(this, xy);
23861                 this.sync();
23862             }
23863         }
23864     },
23865
23866     isVisible : function(){
23867         return this.visible;    
23868     },
23869
23870     // private
23871     showAction : function(){
23872         this.visible = true; // track visibility to prevent getStyle calls
23873         if(this.useDisplay === true){
23874             this.setDisplayed("");
23875         }else if(this.lastXY){
23876             supr.setXY.call(this, this.lastXY);
23877         }else if(this.lastLT){
23878             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23879         }
23880     },
23881
23882     // private
23883     hideAction : function(){
23884         this.visible = false;
23885         if(this.useDisplay === true){
23886             this.setDisplayed(false);
23887         }else{
23888             this.setLeftTop(-10000,-10000);
23889         }
23890     },
23891
23892     // overridden Element method
23893     setVisible : function(v, a, d, c, e){
23894         if(v){
23895             this.showAction();
23896         }
23897         if(a && v){
23898             var cb = function(){
23899                 this.sync(true);
23900                 if(c){
23901                     c();
23902                 }
23903             }.createDelegate(this);
23904             supr.setVisible.call(this, true, true, d, cb, e);
23905         }else{
23906             if(!v){
23907                 this.hideUnders(true);
23908             }
23909             var cb = c;
23910             if(a){
23911                 cb = function(){
23912                     this.hideAction();
23913                     if(c){
23914                         c();
23915                     }
23916                 }.createDelegate(this);
23917             }
23918             supr.setVisible.call(this, v, a, d, cb, e);
23919             if(v){
23920                 this.sync(true);
23921             }else if(!a){
23922                 this.hideAction();
23923             }
23924         }
23925     },
23926
23927     storeXY : function(xy){
23928         delete this.lastLT;
23929         this.lastXY = xy;
23930     },
23931
23932     storeLeftTop : function(left, top){
23933         delete this.lastXY;
23934         this.lastLT = [left, top];
23935     },
23936
23937     // private
23938     beforeFx : function(){
23939         this.beforeAction();
23940         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23941     },
23942
23943     // private
23944     afterFx : function(){
23945         Roo.Layer.superclass.afterFx.apply(this, arguments);
23946         this.sync(this.isVisible());
23947     },
23948
23949     // private
23950     beforeAction : function(){
23951         if(!this.updating && this.shadow){
23952             this.shadow.hide();
23953         }
23954     },
23955
23956     // overridden Element method
23957     setLeft : function(left){
23958         this.storeLeftTop(left, this.getTop(true));
23959         supr.setLeft.apply(this, arguments);
23960         this.sync();
23961     },
23962
23963     setTop : function(top){
23964         this.storeLeftTop(this.getLeft(true), top);
23965         supr.setTop.apply(this, arguments);
23966         this.sync();
23967     },
23968
23969     setLeftTop : function(left, top){
23970         this.storeLeftTop(left, top);
23971         supr.setLeftTop.apply(this, arguments);
23972         this.sync();
23973     },
23974
23975     setXY : function(xy, a, d, c, e){
23976         this.fixDisplay();
23977         this.beforeAction();
23978         this.storeXY(xy);
23979         var cb = this.createCB(c);
23980         supr.setXY.call(this, xy, a, d, cb, e);
23981         if(!a){
23982             cb();
23983         }
23984     },
23985
23986     // private
23987     createCB : function(c){
23988         var el = this;
23989         return function(){
23990             el.constrainXY();
23991             el.sync(true);
23992             if(c){
23993                 c();
23994             }
23995         };
23996     },
23997
23998     // overridden Element method
23999     setX : function(x, a, d, c, e){
24000         this.setXY([x, this.getY()], a, d, c, e);
24001     },
24002
24003     // overridden Element method
24004     setY : function(y, a, d, c, e){
24005         this.setXY([this.getX(), y], a, d, c, e);
24006     },
24007
24008     // overridden Element method
24009     setSize : function(w, h, a, d, c, e){
24010         this.beforeAction();
24011         var cb = this.createCB(c);
24012         supr.setSize.call(this, w, h, a, d, cb, e);
24013         if(!a){
24014             cb();
24015         }
24016     },
24017
24018     // overridden Element method
24019     setWidth : function(w, a, d, c, e){
24020         this.beforeAction();
24021         var cb = this.createCB(c);
24022         supr.setWidth.call(this, w, a, d, cb, e);
24023         if(!a){
24024             cb();
24025         }
24026     },
24027
24028     // overridden Element method
24029     setHeight : function(h, a, d, c, e){
24030         this.beforeAction();
24031         var cb = this.createCB(c);
24032         supr.setHeight.call(this, h, a, d, cb, e);
24033         if(!a){
24034             cb();
24035         }
24036     },
24037
24038     // overridden Element method
24039     setBounds : function(x, y, w, h, a, d, c, e){
24040         this.beforeAction();
24041         var cb = this.createCB(c);
24042         if(!a){
24043             this.storeXY([x, y]);
24044             supr.setXY.call(this, [x, y]);
24045             supr.setSize.call(this, w, h, a, d, cb, e);
24046             cb();
24047         }else{
24048             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24049         }
24050         return this;
24051     },
24052     
24053     /**
24054      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24055      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24056      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24057      * @param {Number} zindex The new z-index to set
24058      * @return {this} The Layer
24059      */
24060     setZIndex : function(zindex){
24061         this.zindex = zindex;
24062         this.setStyle("z-index", zindex + 2);
24063         if(this.shadow){
24064             this.shadow.setZIndex(zindex + 1);
24065         }
24066         if(this.shim){
24067             this.shim.setStyle("z-index", zindex);
24068         }
24069     }
24070 });
24071 })();/*
24072  * Based on:
24073  * Ext JS Library 1.1.1
24074  * Copyright(c) 2006-2007, Ext JS, LLC.
24075  *
24076  * Originally Released Under LGPL - original licence link has changed is not relivant.
24077  *
24078  * Fork - LGPL
24079  * <script type="text/javascript">
24080  */
24081
24082
24083 /**
24084  * @class Roo.Shadow
24085  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24086  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24087  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24088  * @constructor
24089  * Create a new Shadow
24090  * @param {Object} config The config object
24091  */
24092 Roo.Shadow = function(config){
24093     Roo.apply(this, config);
24094     if(typeof this.mode != "string"){
24095         this.mode = this.defaultMode;
24096     }
24097     var o = this.offset, a = {h: 0};
24098     var rad = Math.floor(this.offset/2);
24099     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24100         case "drop":
24101             a.w = 0;
24102             a.l = a.t = o;
24103             a.t -= 1;
24104             if(Roo.isIE){
24105                 a.l -= this.offset + rad;
24106                 a.t -= this.offset + rad;
24107                 a.w -= rad;
24108                 a.h -= rad;
24109                 a.t += 1;
24110             }
24111         break;
24112         case "sides":
24113             a.w = (o*2);
24114             a.l = -o;
24115             a.t = o-1;
24116             if(Roo.isIE){
24117                 a.l -= (this.offset - rad);
24118                 a.t -= this.offset + rad;
24119                 a.l += 1;
24120                 a.w -= (this.offset - rad)*2;
24121                 a.w -= rad + 1;
24122                 a.h -= 1;
24123             }
24124         break;
24125         case "frame":
24126             a.w = a.h = (o*2);
24127             a.l = a.t = -o;
24128             a.t += 1;
24129             a.h -= 2;
24130             if(Roo.isIE){
24131                 a.l -= (this.offset - rad);
24132                 a.t -= (this.offset - rad);
24133                 a.l += 1;
24134                 a.w -= (this.offset + rad + 1);
24135                 a.h -= (this.offset + rad);
24136                 a.h += 1;
24137             }
24138         break;
24139     };
24140
24141     this.adjusts = a;
24142 };
24143
24144 Roo.Shadow.prototype = {
24145     /**
24146      * @cfg {String} mode
24147      * The shadow display mode.  Supports the following options:<br />
24148      * sides: Shadow displays on both sides and bottom only<br />
24149      * frame: Shadow displays equally on all four sides<br />
24150      * drop: Traditional bottom-right drop shadow (default)
24151      */
24152     /**
24153      * @cfg {String} offset
24154      * The number of pixels to offset the shadow from the element (defaults to 4)
24155      */
24156     offset: 4,
24157
24158     // private
24159     defaultMode: "drop",
24160
24161     /**
24162      * Displays the shadow under the target element
24163      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24164      */
24165     show : function(target){
24166         target = Roo.get(target);
24167         if(!this.el){
24168             this.el = Roo.Shadow.Pool.pull();
24169             if(this.el.dom.nextSibling != target.dom){
24170                 this.el.insertBefore(target);
24171             }
24172         }
24173         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24174         if(Roo.isIE){
24175             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24176         }
24177         this.realign(
24178             target.getLeft(true),
24179             target.getTop(true),
24180             target.getWidth(),
24181             target.getHeight()
24182         );
24183         this.el.dom.style.display = "block";
24184     },
24185
24186     /**
24187      * Returns true if the shadow is visible, else false
24188      */
24189     isVisible : function(){
24190         return this.el ? true : false;  
24191     },
24192
24193     /**
24194      * Direct alignment when values are already available. Show must be called at least once before
24195      * calling this method to ensure it is initialized.
24196      * @param {Number} left The target element left position
24197      * @param {Number} top The target element top position
24198      * @param {Number} width The target element width
24199      * @param {Number} height The target element height
24200      */
24201     realign : function(l, t, w, h){
24202         if(!this.el){
24203             return;
24204         }
24205         var a = this.adjusts, d = this.el.dom, s = d.style;
24206         var iea = 0;
24207         s.left = (l+a.l)+"px";
24208         s.top = (t+a.t)+"px";
24209         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24210  
24211         if(s.width != sws || s.height != shs){
24212             s.width = sws;
24213             s.height = shs;
24214             if(!Roo.isIE){
24215                 var cn = d.childNodes;
24216                 var sww = Math.max(0, (sw-12))+"px";
24217                 cn[0].childNodes[1].style.width = sww;
24218                 cn[1].childNodes[1].style.width = sww;
24219                 cn[2].childNodes[1].style.width = sww;
24220                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24221             }
24222         }
24223     },
24224
24225     /**
24226      * Hides this shadow
24227      */
24228     hide : function(){
24229         if(this.el){
24230             this.el.dom.style.display = "none";
24231             Roo.Shadow.Pool.push(this.el);
24232             delete this.el;
24233         }
24234     },
24235
24236     /**
24237      * Adjust the z-index of this shadow
24238      * @param {Number} zindex The new z-index
24239      */
24240     setZIndex : function(z){
24241         this.zIndex = z;
24242         if(this.el){
24243             this.el.setStyle("z-index", z);
24244         }
24245     }
24246 };
24247
24248 // Private utility class that manages the internal Shadow cache
24249 Roo.Shadow.Pool = function(){
24250     var p = [];
24251     var markup = Roo.isIE ?
24252                  '<div class="x-ie-shadow"></div>' :
24253                  '<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>';
24254     return {
24255         pull : function(){
24256             var sh = p.shift();
24257             if(!sh){
24258                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24259                 sh.autoBoxAdjust = false;
24260             }
24261             return sh;
24262         },
24263
24264         push : function(sh){
24265             p.push(sh);
24266         }
24267     };
24268 }();/*
24269  * Based on:
24270  * Ext JS Library 1.1.1
24271  * Copyright(c) 2006-2007, Ext JS, LLC.
24272  *
24273  * Originally Released Under LGPL - original licence link has changed is not relivant.
24274  *
24275  * Fork - LGPL
24276  * <script type="text/javascript">
24277  */
24278
24279
24280 /**
24281  * @class Roo.SplitBar
24282  * @extends Roo.util.Observable
24283  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24284  * <br><br>
24285  * Usage:
24286  * <pre><code>
24287 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24288                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24289 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24290 split.minSize = 100;
24291 split.maxSize = 600;
24292 split.animate = true;
24293 split.on('moved', splitterMoved);
24294 </code></pre>
24295  * @constructor
24296  * Create a new SplitBar
24297  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24298  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24299  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24300  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24301                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24302                         position of the SplitBar).
24303  */
24304 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24305     
24306     /** @private */
24307     this.el = Roo.get(dragElement, true);
24308     this.el.dom.unselectable = "on";
24309     /** @private */
24310     this.resizingEl = Roo.get(resizingElement, true);
24311
24312     /**
24313      * @private
24314      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24315      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24316      * @type Number
24317      */
24318     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24319     
24320     /**
24321      * The minimum size of the resizing element. (Defaults to 0)
24322      * @type Number
24323      */
24324     this.minSize = 0;
24325     
24326     /**
24327      * The maximum size of the resizing element. (Defaults to 2000)
24328      * @type Number
24329      */
24330     this.maxSize = 2000;
24331     
24332     /**
24333      * Whether to animate the transition to the new size
24334      * @type Boolean
24335      */
24336     this.animate = false;
24337     
24338     /**
24339      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24340      * @type Boolean
24341      */
24342     this.useShim = false;
24343     
24344     /** @private */
24345     this.shim = null;
24346     
24347     if(!existingProxy){
24348         /** @private */
24349         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24350     }else{
24351         this.proxy = Roo.get(existingProxy).dom;
24352     }
24353     /** @private */
24354     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24355     
24356     /** @private */
24357     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24358     
24359     /** @private */
24360     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24361     
24362     /** @private */
24363     this.dragSpecs = {};
24364     
24365     /**
24366      * @private The adapter to use to positon and resize elements
24367      */
24368     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24369     this.adapter.init(this);
24370     
24371     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24372         /** @private */
24373         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24374         this.el.addClass("x-splitbar-h");
24375     }else{
24376         /** @private */
24377         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24378         this.el.addClass("x-splitbar-v");
24379     }
24380     
24381     this.addEvents({
24382         /**
24383          * @event resize
24384          * Fires when the splitter is moved (alias for {@link #event-moved})
24385          * @param {Roo.SplitBar} this
24386          * @param {Number} newSize the new width or height
24387          */
24388         "resize" : true,
24389         /**
24390          * @event moved
24391          * Fires when the splitter is moved
24392          * @param {Roo.SplitBar} this
24393          * @param {Number} newSize the new width or height
24394          */
24395         "moved" : true,
24396         /**
24397          * @event beforeresize
24398          * Fires before the splitter is dragged
24399          * @param {Roo.SplitBar} this
24400          */
24401         "beforeresize" : true,
24402
24403         "beforeapply" : true
24404     });
24405
24406     Roo.util.Observable.call(this);
24407 };
24408
24409 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24410     onStartProxyDrag : function(x, y){
24411         this.fireEvent("beforeresize", this);
24412         if(!this.overlay){
24413             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24414             o.unselectable();
24415             o.enableDisplayMode("block");
24416             // all splitbars share the same overlay
24417             Roo.SplitBar.prototype.overlay = o;
24418         }
24419         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24420         this.overlay.show();
24421         Roo.get(this.proxy).setDisplayed("block");
24422         var size = this.adapter.getElementSize(this);
24423         this.activeMinSize = this.getMinimumSize();;
24424         this.activeMaxSize = this.getMaximumSize();;
24425         var c1 = size - this.activeMinSize;
24426         var c2 = Math.max(this.activeMaxSize - size, 0);
24427         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24428             this.dd.resetConstraints();
24429             this.dd.setXConstraint(
24430                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24431                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24432             );
24433             this.dd.setYConstraint(0, 0);
24434         }else{
24435             this.dd.resetConstraints();
24436             this.dd.setXConstraint(0, 0);
24437             this.dd.setYConstraint(
24438                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24439                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24440             );
24441          }
24442         this.dragSpecs.startSize = size;
24443         this.dragSpecs.startPoint = [x, y];
24444         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24445     },
24446     
24447     /** 
24448      * @private Called after the drag operation by the DDProxy
24449      */
24450     onEndProxyDrag : function(e){
24451         Roo.get(this.proxy).setDisplayed(false);
24452         var endPoint = Roo.lib.Event.getXY(e);
24453         if(this.overlay){
24454             this.overlay.hide();
24455         }
24456         var newSize;
24457         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24458             newSize = this.dragSpecs.startSize + 
24459                 (this.placement == Roo.SplitBar.LEFT ?
24460                     endPoint[0] - this.dragSpecs.startPoint[0] :
24461                     this.dragSpecs.startPoint[0] - endPoint[0]
24462                 );
24463         }else{
24464             newSize = this.dragSpecs.startSize + 
24465                 (this.placement == Roo.SplitBar.TOP ?
24466                     endPoint[1] - this.dragSpecs.startPoint[1] :
24467                     this.dragSpecs.startPoint[1] - endPoint[1]
24468                 );
24469         }
24470         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24471         if(newSize != this.dragSpecs.startSize){
24472             if(this.fireEvent('beforeapply', this, newSize) !== false){
24473                 this.adapter.setElementSize(this, newSize);
24474                 this.fireEvent("moved", this, newSize);
24475                 this.fireEvent("resize", this, newSize);
24476             }
24477         }
24478     },
24479     
24480     /**
24481      * Get the adapter this SplitBar uses
24482      * @return The adapter object
24483      */
24484     getAdapter : function(){
24485         return this.adapter;
24486     },
24487     
24488     /**
24489      * Set the adapter this SplitBar uses
24490      * @param {Object} adapter A SplitBar adapter object
24491      */
24492     setAdapter : function(adapter){
24493         this.adapter = adapter;
24494         this.adapter.init(this);
24495     },
24496     
24497     /**
24498      * Gets the minimum size for the resizing element
24499      * @return {Number} The minimum size
24500      */
24501     getMinimumSize : function(){
24502         return this.minSize;
24503     },
24504     
24505     /**
24506      * Sets the minimum size for the resizing element
24507      * @param {Number} minSize The minimum size
24508      */
24509     setMinimumSize : function(minSize){
24510         this.minSize = minSize;
24511     },
24512     
24513     /**
24514      * Gets the maximum size for the resizing element
24515      * @return {Number} The maximum size
24516      */
24517     getMaximumSize : function(){
24518         return this.maxSize;
24519     },
24520     
24521     /**
24522      * Sets the maximum size for the resizing element
24523      * @param {Number} maxSize The maximum size
24524      */
24525     setMaximumSize : function(maxSize){
24526         this.maxSize = maxSize;
24527     },
24528     
24529     /**
24530      * Sets the initialize size for the resizing element
24531      * @param {Number} size The initial size
24532      */
24533     setCurrentSize : function(size){
24534         var oldAnimate = this.animate;
24535         this.animate = false;
24536         this.adapter.setElementSize(this, size);
24537         this.animate = oldAnimate;
24538     },
24539     
24540     /**
24541      * Destroy this splitbar. 
24542      * @param {Boolean} removeEl True to remove the element
24543      */
24544     destroy : function(removeEl){
24545         if(this.shim){
24546             this.shim.remove();
24547         }
24548         this.dd.unreg();
24549         this.proxy.parentNode.removeChild(this.proxy);
24550         if(removeEl){
24551             this.el.remove();
24552         }
24553     }
24554 });
24555
24556 /**
24557  * @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.
24558  */
24559 Roo.SplitBar.createProxy = function(dir){
24560     var proxy = new Roo.Element(document.createElement("div"));
24561     proxy.unselectable();
24562     var cls = 'x-splitbar-proxy';
24563     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24564     document.body.appendChild(proxy.dom);
24565     return proxy.dom;
24566 };
24567
24568 /** 
24569  * @class Roo.SplitBar.BasicLayoutAdapter
24570  * Default Adapter. It assumes the splitter and resizing element are not positioned
24571  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24572  */
24573 Roo.SplitBar.BasicLayoutAdapter = function(){
24574 };
24575
24576 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24577     // do nothing for now
24578     init : function(s){
24579     
24580     },
24581     /**
24582      * Called before drag operations to get the current size of the resizing element. 
24583      * @param {Roo.SplitBar} s The SplitBar using this adapter
24584      */
24585      getElementSize : function(s){
24586         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24587             return s.resizingEl.getWidth();
24588         }else{
24589             return s.resizingEl.getHeight();
24590         }
24591     },
24592     
24593     /**
24594      * Called after drag operations to set the size of the resizing element.
24595      * @param {Roo.SplitBar} s The SplitBar using this adapter
24596      * @param {Number} newSize The new size to set
24597      * @param {Function} onComplete A function to be invoked when resizing is complete
24598      */
24599     setElementSize : function(s, newSize, onComplete){
24600         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24601             if(!s.animate){
24602                 s.resizingEl.setWidth(newSize);
24603                 if(onComplete){
24604                     onComplete(s, newSize);
24605                 }
24606             }else{
24607                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24608             }
24609         }else{
24610             
24611             if(!s.animate){
24612                 s.resizingEl.setHeight(newSize);
24613                 if(onComplete){
24614                     onComplete(s, newSize);
24615                 }
24616             }else{
24617                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24618             }
24619         }
24620     }
24621 };
24622
24623 /** 
24624  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24625  * @extends Roo.SplitBar.BasicLayoutAdapter
24626  * Adapter that  moves the splitter element to align with the resized sizing element. 
24627  * Used with an absolute positioned SplitBar.
24628  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24629  * document.body, make sure you assign an id to the body element.
24630  */
24631 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24632     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24633     this.container = Roo.get(container);
24634 };
24635
24636 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24637     init : function(s){
24638         this.basic.init(s);
24639     },
24640     
24641     getElementSize : function(s){
24642         return this.basic.getElementSize(s);
24643     },
24644     
24645     setElementSize : function(s, newSize, onComplete){
24646         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24647     },
24648     
24649     moveSplitter : function(s){
24650         var yes = Roo.SplitBar;
24651         switch(s.placement){
24652             case yes.LEFT:
24653                 s.el.setX(s.resizingEl.getRight());
24654                 break;
24655             case yes.RIGHT:
24656                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24657                 break;
24658             case yes.TOP:
24659                 s.el.setY(s.resizingEl.getBottom());
24660                 break;
24661             case yes.BOTTOM:
24662                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24663                 break;
24664         }
24665     }
24666 };
24667
24668 /**
24669  * Orientation constant - Create a vertical SplitBar
24670  * @static
24671  * @type Number
24672  */
24673 Roo.SplitBar.VERTICAL = 1;
24674
24675 /**
24676  * Orientation constant - Create a horizontal SplitBar
24677  * @static
24678  * @type Number
24679  */
24680 Roo.SplitBar.HORIZONTAL = 2;
24681
24682 /**
24683  * Placement constant - The resizing element is to the left of the splitter element
24684  * @static
24685  * @type Number
24686  */
24687 Roo.SplitBar.LEFT = 1;
24688
24689 /**
24690  * Placement constant - The resizing element is to the right of the splitter element
24691  * @static
24692  * @type Number
24693  */
24694 Roo.SplitBar.RIGHT = 2;
24695
24696 /**
24697  * Placement constant - The resizing element is positioned above the splitter element
24698  * @static
24699  * @type Number
24700  */
24701 Roo.SplitBar.TOP = 3;
24702
24703 /**
24704  * Placement constant - The resizing element is positioned under splitter element
24705  * @static
24706  * @type Number
24707  */
24708 Roo.SplitBar.BOTTOM = 4;
24709 /*
24710  * Based on:
24711  * Ext JS Library 1.1.1
24712  * Copyright(c) 2006-2007, Ext JS, LLC.
24713  *
24714  * Originally Released Under LGPL - original licence link has changed is not relivant.
24715  *
24716  * Fork - LGPL
24717  * <script type="text/javascript">
24718  */
24719
24720 /**
24721  * @class Roo.View
24722  * @extends Roo.util.Observable
24723  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24724  * This class also supports single and multi selection modes. <br>
24725  * Create a data model bound view:
24726  <pre><code>
24727  var store = new Roo.data.Store(...);
24728
24729  var view = new Roo.View({
24730     el : "my-element",
24731     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24732  
24733     singleSelect: true,
24734     selectedClass: "ydataview-selected",
24735     store: store
24736  });
24737
24738  // listen for node click?
24739  view.on("click", function(vw, index, node, e){
24740  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24741  });
24742
24743  // load XML data
24744  dataModel.load("foobar.xml");
24745  </code></pre>
24746  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24747  * <br><br>
24748  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24749  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24750  * 
24751  * Note: old style constructor is still suported (container, template, config)
24752  * 
24753  * @constructor
24754  * Create a new View
24755  * @param {Object} config The config object
24756  * 
24757  */
24758 Roo.View = function(config, depreciated_tpl, depreciated_config){
24759     
24760     if (typeof(depreciated_tpl) == 'undefined') {
24761         // new way.. - universal constructor.
24762         Roo.apply(this, config);
24763         this.el  = Roo.get(this.el);
24764     } else {
24765         // old format..
24766         this.el  = Roo.get(config);
24767         this.tpl = depreciated_tpl;
24768         Roo.apply(this, depreciated_config);
24769     }
24770     this.wrapEl  = this.el.wrap().wrap();
24771     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24772     
24773     
24774     if(typeof(this.tpl) == "string"){
24775         this.tpl = new Roo.Template(this.tpl);
24776     } else {
24777         // support xtype ctors..
24778         this.tpl = new Roo.factory(this.tpl, Roo);
24779     }
24780     
24781     
24782     this.tpl.compile();
24783    
24784   
24785     
24786      
24787     /** @private */
24788     this.addEvents({
24789         /**
24790          * @event beforeclick
24791          * Fires before a click is processed. Returns false to cancel the default action.
24792          * @param {Roo.View} this
24793          * @param {Number} index The index of the target node
24794          * @param {HTMLElement} node The target node
24795          * @param {Roo.EventObject} e The raw event object
24796          */
24797             "beforeclick" : true,
24798         /**
24799          * @event click
24800          * Fires when a template node is clicked.
24801          * @param {Roo.View} this
24802          * @param {Number} index The index of the target node
24803          * @param {HTMLElement} node The target node
24804          * @param {Roo.EventObject} e The raw event object
24805          */
24806             "click" : true,
24807         /**
24808          * @event dblclick
24809          * Fires when a template node is double clicked.
24810          * @param {Roo.View} this
24811          * @param {Number} index The index of the target node
24812          * @param {HTMLElement} node The target node
24813          * @param {Roo.EventObject} e The raw event object
24814          */
24815             "dblclick" : true,
24816         /**
24817          * @event contextmenu
24818          * Fires when a template node is right clicked.
24819          * @param {Roo.View} this
24820          * @param {Number} index The index of the target node
24821          * @param {HTMLElement} node The target node
24822          * @param {Roo.EventObject} e The raw event object
24823          */
24824             "contextmenu" : true,
24825         /**
24826          * @event selectionchange
24827          * Fires when the selected nodes change.
24828          * @param {Roo.View} this
24829          * @param {Array} selections Array of the selected nodes
24830          */
24831             "selectionchange" : true,
24832     
24833         /**
24834          * @event beforeselect
24835          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24836          * @param {Roo.View} this
24837          * @param {HTMLElement} node The node to be selected
24838          * @param {Array} selections Array of currently selected nodes
24839          */
24840             "beforeselect" : true,
24841         /**
24842          * @event preparedata
24843          * Fires on every row to render, to allow you to change the data.
24844          * @param {Roo.View} this
24845          * @param {Object} data to be rendered (change this)
24846          */
24847           "preparedata" : true
24848           
24849           
24850         });
24851
24852
24853
24854     this.el.on({
24855         "click": this.onClick,
24856         "dblclick": this.onDblClick,
24857         "contextmenu": this.onContextMenu,
24858         scope:this
24859     });
24860
24861     this.selections = [];
24862     this.nodes = [];
24863     this.cmp = new Roo.CompositeElementLite([]);
24864     if(this.store){
24865         this.store = Roo.factory(this.store, Roo.data);
24866         this.setStore(this.store, true);
24867     }
24868     
24869     if ( this.footer && this.footer.xtype) {
24870            
24871          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24872         
24873         this.footer.dataSource = this.store
24874         this.footer.container = fctr;
24875         this.footer = Roo.factory(this.footer, Roo);
24876         fctr.insertFirst(this.el);
24877         
24878         // this is a bit insane - as the paging toolbar seems to detach the el..
24879 //        dom.parentNode.parentNode.parentNode
24880          // they get detached?
24881     }
24882     
24883     
24884     Roo.View.superclass.constructor.call(this);
24885     
24886     
24887 };
24888
24889 Roo.extend(Roo.View, Roo.util.Observable, {
24890     
24891      /**
24892      * @cfg {Roo.data.Store} store Data store to load data from.
24893      */
24894     store : false,
24895     
24896     /**
24897      * @cfg {String|Roo.Element} el The container element.
24898      */
24899     el : '',
24900     
24901     /**
24902      * @cfg {String|Roo.Template} tpl The template used by this View 
24903      */
24904     tpl : false,
24905     /**
24906      * @cfg {String} dataName the named area of the template to use as the data area
24907      *                          Works with domtemplates roo-name="name"
24908      */
24909     dataName: false,
24910     /**
24911      * @cfg {String} selectedClass The css class to add to selected nodes
24912      */
24913     selectedClass : "x-view-selected",
24914      /**
24915      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24916      */
24917     emptyText : "",
24918     
24919     /**
24920      * @cfg {String} text to display on mask (default Loading)
24921      */
24922     mask : false,
24923     /**
24924      * @cfg {Boolean} multiSelect Allow multiple selection
24925      */
24926     multiSelect : false,
24927     /**
24928      * @cfg {Boolean} singleSelect Allow single selection
24929      */
24930     singleSelect:  false,
24931     
24932     /**
24933      * @cfg {Boolean} toggleSelect - selecting 
24934      */
24935     toggleSelect : false,
24936     
24937     /**
24938      * Returns the element this view is bound to.
24939      * @return {Roo.Element}
24940      */
24941     getEl : function(){
24942         return this.wrapEl;
24943     },
24944     
24945     
24946
24947     /**
24948      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24949      */
24950     refresh : function(){
24951         Roo.log('refresh');
24952         var t = this.tpl;
24953         
24954         // if we are using something like 'domtemplate', then
24955         // the what gets used is:
24956         // t.applySubtemplate(NAME, data, wrapping data..)
24957         // the outer template then get' applied with
24958         //     the store 'extra data'
24959         // and the body get's added to the
24960         //      roo-name="data" node?
24961         //      <span class='roo-tpl-{name}'></span> ?????
24962         
24963         
24964         
24965         this.clearSelections();
24966         this.el.update("");
24967         var html = [];
24968         var records = this.store.getRange();
24969         if(records.length < 1) {
24970             
24971             // is this valid??  = should it render a template??
24972             
24973             this.el.update(this.emptyText);
24974             return;
24975         }
24976         var el = this.el;
24977         if (this.dataName) {
24978             this.el.update(t.apply(this.store.meta)); //????
24979             el = this.el.child('.roo-tpl-' + this.dataName);
24980         }
24981         
24982         for(var i = 0, len = records.length; i < len; i++){
24983             var data = this.prepareData(records[i].data, i, records[i]);
24984             this.fireEvent("preparedata", this, data, i, records[i]);
24985             html[html.length] = Roo.util.Format.trim(
24986                 this.dataName ?
24987                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24988                     t.apply(data)
24989             );
24990         }
24991         
24992         
24993         
24994         el.update(html.join(""));
24995         this.nodes = el.dom.childNodes;
24996         this.updateIndexes(0);
24997     },
24998     
24999
25000     /**
25001      * Function to override to reformat the data that is sent to
25002      * the template for each node.
25003      * DEPRICATED - use the preparedata event handler.
25004      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25005      * a JSON object for an UpdateManager bound view).
25006      */
25007     prepareData : function(data, index, record)
25008     {
25009         this.fireEvent("preparedata", this, data, index, record);
25010         return data;
25011     },
25012
25013     onUpdate : function(ds, record){
25014          Roo.log('on update');   
25015         this.clearSelections();
25016         var index = this.store.indexOf(record);
25017         var n = this.nodes[index];
25018         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25019         n.parentNode.removeChild(n);
25020         this.updateIndexes(index, index);
25021     },
25022
25023     
25024     
25025 // --------- FIXME     
25026     onAdd : function(ds, records, index)
25027     {
25028         Roo.log(['on Add', ds, records, index] );        
25029         this.clearSelections();
25030         if(this.nodes.length == 0){
25031             this.refresh();
25032             return;
25033         }
25034         var n = this.nodes[index];
25035         for(var i = 0, len = records.length; i < len; i++){
25036             var d = this.prepareData(records[i].data, i, records[i]);
25037             if(n){
25038                 this.tpl.insertBefore(n, d);
25039             }else{
25040                 
25041                 this.tpl.append(this.el, d);
25042             }
25043         }
25044         this.updateIndexes(index);
25045     },
25046
25047     onRemove : function(ds, record, index){
25048         Roo.log('onRemove');
25049         this.clearSelections();
25050         var el = this.dataName  ?
25051             this.el.child('.roo-tpl-' + this.dataName) :
25052             this.el; 
25053         
25054         el.dom.removeChild(this.nodes[index]);
25055         this.updateIndexes(index);
25056     },
25057
25058     /**
25059      * Refresh an individual node.
25060      * @param {Number} index
25061      */
25062     refreshNode : function(index){
25063         this.onUpdate(this.store, this.store.getAt(index));
25064     },
25065
25066     updateIndexes : function(startIndex, endIndex){
25067         var ns = this.nodes;
25068         startIndex = startIndex || 0;
25069         endIndex = endIndex || ns.length - 1;
25070         for(var i = startIndex; i <= endIndex; i++){
25071             ns[i].nodeIndex = i;
25072         }
25073     },
25074
25075     /**
25076      * Changes the data store this view uses and refresh the view.
25077      * @param {Store} store
25078      */
25079     setStore : function(store, initial){
25080         if(!initial && this.store){
25081             this.store.un("datachanged", this.refresh);
25082             this.store.un("add", this.onAdd);
25083             this.store.un("remove", this.onRemove);
25084             this.store.un("update", this.onUpdate);
25085             this.store.un("clear", this.refresh);
25086             this.store.un("beforeload", this.onBeforeLoad);
25087             this.store.un("load", this.onLoad);
25088             this.store.un("loadexception", this.onLoad);
25089         }
25090         if(store){
25091           
25092             store.on("datachanged", this.refresh, this);
25093             store.on("add", this.onAdd, this);
25094             store.on("remove", this.onRemove, this);
25095             store.on("update", this.onUpdate, this);
25096             store.on("clear", this.refresh, this);
25097             store.on("beforeload", this.onBeforeLoad, this);
25098             store.on("load", this.onLoad, this);
25099             store.on("loadexception", this.onLoad, this);
25100         }
25101         
25102         if(store){
25103             this.refresh();
25104         }
25105     },
25106     /**
25107      * onbeforeLoad - masks the loading area.
25108      *
25109      */
25110     onBeforeLoad : function(store,opts)
25111     {
25112          Roo.log('onBeforeLoad');   
25113         if (!opts.add) {
25114             this.el.update("");
25115         }
25116         this.el.mask(this.mask ? this.mask : "Loading" ); 
25117     },
25118     onLoad : function ()
25119     {
25120         this.el.unmask();
25121     },
25122     
25123
25124     /**
25125      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25126      * @param {HTMLElement} node
25127      * @return {HTMLElement} The template node
25128      */
25129     findItemFromChild : function(node){
25130         var el = this.dataName  ?
25131             this.el.child('.roo-tpl-' + this.dataName,true) :
25132             this.el.dom; 
25133         
25134         if(!node || node.parentNode == el){
25135                     return node;
25136             }
25137             var p = node.parentNode;
25138             while(p && p != el){
25139             if(p.parentNode == el){
25140                 return p;
25141             }
25142             p = p.parentNode;
25143         }
25144             return null;
25145     },
25146
25147     /** @ignore */
25148     onClick : function(e){
25149         var item = this.findItemFromChild(e.getTarget());
25150         if(item){
25151             var index = this.indexOf(item);
25152             if(this.onItemClick(item, index, e) !== false){
25153                 this.fireEvent("click", this, index, item, e);
25154             }
25155         }else{
25156             this.clearSelections();
25157         }
25158     },
25159
25160     /** @ignore */
25161     onContextMenu : function(e){
25162         var item = this.findItemFromChild(e.getTarget());
25163         if(item){
25164             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25165         }
25166     },
25167
25168     /** @ignore */
25169     onDblClick : function(e){
25170         var item = this.findItemFromChild(e.getTarget());
25171         if(item){
25172             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25173         }
25174     },
25175
25176     onItemClick : function(item, index, e)
25177     {
25178         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25179             return false;
25180         }
25181         if (this.toggleSelect) {
25182             var m = this.isSelected(item) ? 'unselect' : 'select';
25183             Roo.log(m);
25184             var _t = this;
25185             _t[m](item, true, false);
25186             return true;
25187         }
25188         if(this.multiSelect || this.singleSelect){
25189             if(this.multiSelect && e.shiftKey && this.lastSelection){
25190                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25191             }else{
25192                 this.select(item, this.multiSelect && e.ctrlKey);
25193                 this.lastSelection = item;
25194             }
25195             e.preventDefault();
25196         }
25197         return true;
25198     },
25199
25200     /**
25201      * Get the number of selected nodes.
25202      * @return {Number}
25203      */
25204     getSelectionCount : function(){
25205         return this.selections.length;
25206     },
25207
25208     /**
25209      * Get the currently selected nodes.
25210      * @return {Array} An array of HTMLElements
25211      */
25212     getSelectedNodes : function(){
25213         return this.selections;
25214     },
25215
25216     /**
25217      * Get the indexes of the selected nodes.
25218      * @return {Array}
25219      */
25220     getSelectedIndexes : function(){
25221         var indexes = [], s = this.selections;
25222         for(var i = 0, len = s.length; i < len; i++){
25223             indexes.push(s[i].nodeIndex);
25224         }
25225         return indexes;
25226     },
25227
25228     /**
25229      * Clear all selections
25230      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25231      */
25232     clearSelections : function(suppressEvent){
25233         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25234             this.cmp.elements = this.selections;
25235             this.cmp.removeClass(this.selectedClass);
25236             this.selections = [];
25237             if(!suppressEvent){
25238                 this.fireEvent("selectionchange", this, this.selections);
25239             }
25240         }
25241     },
25242
25243     /**
25244      * Returns true if the passed node is selected
25245      * @param {HTMLElement/Number} node The node or node index
25246      * @return {Boolean}
25247      */
25248     isSelected : function(node){
25249         var s = this.selections;
25250         if(s.length < 1){
25251             return false;
25252         }
25253         node = this.getNode(node);
25254         return s.indexOf(node) !== -1;
25255     },
25256
25257     /**
25258      * Selects nodes.
25259      * @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
25260      * @param {Boolean} keepExisting (optional) true to keep existing selections
25261      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25262      */
25263     select : function(nodeInfo, keepExisting, suppressEvent){
25264         if(nodeInfo instanceof Array){
25265             if(!keepExisting){
25266                 this.clearSelections(true);
25267             }
25268             for(var i = 0, len = nodeInfo.length; i < len; i++){
25269                 this.select(nodeInfo[i], true, true);
25270             }
25271             return;
25272         } 
25273         var node = this.getNode(nodeInfo);
25274         if(!node || this.isSelected(node)){
25275             return; // already selected.
25276         }
25277         if(!keepExisting){
25278             this.clearSelections(true);
25279         }
25280         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25281             Roo.fly(node).addClass(this.selectedClass);
25282             this.selections.push(node);
25283             if(!suppressEvent){
25284                 this.fireEvent("selectionchange", this, this.selections);
25285             }
25286         }
25287         
25288         
25289     },
25290       /**
25291      * Unselects nodes.
25292      * @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
25293      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25294      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25295      */
25296     unselect : function(nodeInfo, keepExisting, suppressEvent)
25297     {
25298         if(nodeInfo instanceof Array){
25299             Roo.each(this.selections, function(s) {
25300                 this.unselect(s, nodeInfo);
25301             }, this);
25302             return;
25303         }
25304         var node = this.getNode(nodeInfo);
25305         if(!node || !this.isSelected(node)){
25306             Roo.log("not selected");
25307             return; // not selected.
25308         }
25309         // fireevent???
25310         var ns = [];
25311         Roo.each(this.selections, function(s) {
25312             if (s == node ) {
25313                 Roo.fly(node).removeClass(this.selectedClass);
25314
25315                 return;
25316             }
25317             ns.push(s);
25318         },this);
25319         
25320         this.selections= ns;
25321         this.fireEvent("selectionchange", this, this.selections);
25322     },
25323
25324     /**
25325      * Gets a template node.
25326      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25327      * @return {HTMLElement} The node or null if it wasn't found
25328      */
25329     getNode : function(nodeInfo){
25330         if(typeof nodeInfo == "string"){
25331             return document.getElementById(nodeInfo);
25332         }else if(typeof nodeInfo == "number"){
25333             return this.nodes[nodeInfo];
25334         }
25335         return nodeInfo;
25336     },
25337
25338     /**
25339      * Gets a range template nodes.
25340      * @param {Number} startIndex
25341      * @param {Number} endIndex
25342      * @return {Array} An array of nodes
25343      */
25344     getNodes : function(start, end){
25345         var ns = this.nodes;
25346         start = start || 0;
25347         end = typeof end == "undefined" ? ns.length - 1 : end;
25348         var nodes = [];
25349         if(start <= end){
25350             for(var i = start; i <= end; i++){
25351                 nodes.push(ns[i]);
25352             }
25353         } else{
25354             for(var i = start; i >= end; i--){
25355                 nodes.push(ns[i]);
25356             }
25357         }
25358         return nodes;
25359     },
25360
25361     /**
25362      * Finds the index of the passed node
25363      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25364      * @return {Number} The index of the node or -1
25365      */
25366     indexOf : function(node){
25367         node = this.getNode(node);
25368         if(typeof node.nodeIndex == "number"){
25369             return node.nodeIndex;
25370         }
25371         var ns = this.nodes;
25372         for(var i = 0, len = ns.length; i < len; i++){
25373             if(ns[i] == node){
25374                 return i;
25375             }
25376         }
25377         return -1;
25378     }
25379 });
25380 /*
25381  * Based on:
25382  * Ext JS Library 1.1.1
25383  * Copyright(c) 2006-2007, Ext JS, LLC.
25384  *
25385  * Originally Released Under LGPL - original licence link has changed is not relivant.
25386  *
25387  * Fork - LGPL
25388  * <script type="text/javascript">
25389  */
25390
25391 /**
25392  * @class Roo.JsonView
25393  * @extends Roo.View
25394  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25395 <pre><code>
25396 var view = new Roo.JsonView({
25397     container: "my-element",
25398     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25399     multiSelect: true, 
25400     jsonRoot: "data" 
25401 });
25402
25403 // listen for node click?
25404 view.on("click", function(vw, index, node, e){
25405     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25406 });
25407
25408 // direct load of JSON data
25409 view.load("foobar.php");
25410
25411 // Example from my blog list
25412 var tpl = new Roo.Template(
25413     '&lt;div class="entry"&gt;' +
25414     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25415     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25416     "&lt;/div&gt;&lt;hr /&gt;"
25417 );
25418
25419 var moreView = new Roo.JsonView({
25420     container :  "entry-list", 
25421     template : tpl,
25422     jsonRoot: "posts"
25423 });
25424 moreView.on("beforerender", this.sortEntries, this);
25425 moreView.load({
25426     url: "/blog/get-posts.php",
25427     params: "allposts=true",
25428     text: "Loading Blog Entries..."
25429 });
25430 </code></pre>
25431
25432 * Note: old code is supported with arguments : (container, template, config)
25433
25434
25435  * @constructor
25436  * Create a new JsonView
25437  * 
25438  * @param {Object} config The config object
25439  * 
25440  */
25441 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25442     
25443     
25444     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25445
25446     var um = this.el.getUpdateManager();
25447     um.setRenderer(this);
25448     um.on("update", this.onLoad, this);
25449     um.on("failure", this.onLoadException, this);
25450
25451     /**
25452      * @event beforerender
25453      * Fires before rendering of the downloaded JSON data.
25454      * @param {Roo.JsonView} this
25455      * @param {Object} data The JSON data loaded
25456      */
25457     /**
25458      * @event load
25459      * Fires when data is loaded.
25460      * @param {Roo.JsonView} this
25461      * @param {Object} data The JSON data loaded
25462      * @param {Object} response The raw Connect response object
25463      */
25464     /**
25465      * @event loadexception
25466      * Fires when loading fails.
25467      * @param {Roo.JsonView} this
25468      * @param {Object} response The raw Connect response object
25469      */
25470     this.addEvents({
25471         'beforerender' : true,
25472         'load' : true,
25473         'loadexception' : true
25474     });
25475 };
25476 Roo.extend(Roo.JsonView, Roo.View, {
25477     /**
25478      * @type {String} The root property in the loaded JSON object that contains the data
25479      */
25480     jsonRoot : "",
25481
25482     /**
25483      * Refreshes the view.
25484      */
25485     refresh : function(){
25486         this.clearSelections();
25487         this.el.update("");
25488         var html = [];
25489         var o = this.jsonData;
25490         if(o && o.length > 0){
25491             for(var i = 0, len = o.length; i < len; i++){
25492                 var data = this.prepareData(o[i], i, o);
25493                 html[html.length] = this.tpl.apply(data);
25494             }
25495         }else{
25496             html.push(this.emptyText);
25497         }
25498         this.el.update(html.join(""));
25499         this.nodes = this.el.dom.childNodes;
25500         this.updateIndexes(0);
25501     },
25502
25503     /**
25504      * 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.
25505      * @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:
25506      <pre><code>
25507      view.load({
25508          url: "your-url.php",
25509          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25510          callback: yourFunction,
25511          scope: yourObject, //(optional scope)
25512          discardUrl: false,
25513          nocache: false,
25514          text: "Loading...",
25515          timeout: 30,
25516          scripts: false
25517      });
25518      </code></pre>
25519      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25520      * 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.
25521      * @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}
25522      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25523      * @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.
25524      */
25525     load : function(){
25526         var um = this.el.getUpdateManager();
25527         um.update.apply(um, arguments);
25528     },
25529
25530     render : function(el, response){
25531         this.clearSelections();
25532         this.el.update("");
25533         var o;
25534         try{
25535             o = Roo.util.JSON.decode(response.responseText);
25536             if(this.jsonRoot){
25537                 
25538                 o = o[this.jsonRoot];
25539             }
25540         } catch(e){
25541         }
25542         /**
25543          * The current JSON data or null
25544          */
25545         this.jsonData = o;
25546         this.beforeRender();
25547         this.refresh();
25548     },
25549
25550 /**
25551  * Get the number of records in the current JSON dataset
25552  * @return {Number}
25553  */
25554     getCount : function(){
25555         return this.jsonData ? this.jsonData.length : 0;
25556     },
25557
25558 /**
25559  * Returns the JSON object for the specified node(s)
25560  * @param {HTMLElement/Array} node The node or an array of nodes
25561  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25562  * you get the JSON object for the node
25563  */
25564     getNodeData : function(node){
25565         if(node instanceof Array){
25566             var data = [];
25567             for(var i = 0, len = node.length; i < len; i++){
25568                 data.push(this.getNodeData(node[i]));
25569             }
25570             return data;
25571         }
25572         return this.jsonData[this.indexOf(node)] || null;
25573     },
25574
25575     beforeRender : function(){
25576         this.snapshot = this.jsonData;
25577         if(this.sortInfo){
25578             this.sort.apply(this, this.sortInfo);
25579         }
25580         this.fireEvent("beforerender", this, this.jsonData);
25581     },
25582
25583     onLoad : function(el, o){
25584         this.fireEvent("load", this, this.jsonData, o);
25585     },
25586
25587     onLoadException : function(el, o){
25588         this.fireEvent("loadexception", this, o);
25589     },
25590
25591 /**
25592  * Filter the data by a specific property.
25593  * @param {String} property A property on your JSON objects
25594  * @param {String/RegExp} value Either string that the property values
25595  * should start with, or a RegExp to test against the property
25596  */
25597     filter : function(property, value){
25598         if(this.jsonData){
25599             var data = [];
25600             var ss = this.snapshot;
25601             if(typeof value == "string"){
25602                 var vlen = value.length;
25603                 if(vlen == 0){
25604                     this.clearFilter();
25605                     return;
25606                 }
25607                 value = value.toLowerCase();
25608                 for(var i = 0, len = ss.length; i < len; i++){
25609                     var o = ss[i];
25610                     if(o[property].substr(0, vlen).toLowerCase() == value){
25611                         data.push(o);
25612                     }
25613                 }
25614             } else if(value.exec){ // regex?
25615                 for(var i = 0, len = ss.length; i < len; i++){
25616                     var o = ss[i];
25617                     if(value.test(o[property])){
25618                         data.push(o);
25619                     }
25620                 }
25621             } else{
25622                 return;
25623             }
25624             this.jsonData = data;
25625             this.refresh();
25626         }
25627     },
25628
25629 /**
25630  * Filter by a function. The passed function will be called with each
25631  * object in the current dataset. If the function returns true the value is kept,
25632  * otherwise it is filtered.
25633  * @param {Function} fn
25634  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25635  */
25636     filterBy : function(fn, scope){
25637         if(this.jsonData){
25638             var data = [];
25639             var ss = this.snapshot;
25640             for(var i = 0, len = ss.length; i < len; i++){
25641                 var o = ss[i];
25642                 if(fn.call(scope || this, o)){
25643                     data.push(o);
25644                 }
25645             }
25646             this.jsonData = data;
25647             this.refresh();
25648         }
25649     },
25650
25651 /**
25652  * Clears the current filter.
25653  */
25654     clearFilter : function(){
25655         if(this.snapshot && this.jsonData != this.snapshot){
25656             this.jsonData = this.snapshot;
25657             this.refresh();
25658         }
25659     },
25660
25661
25662 /**
25663  * Sorts the data for this view and refreshes it.
25664  * @param {String} property A property on your JSON objects to sort on
25665  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25666  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25667  */
25668     sort : function(property, dir, sortType){
25669         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25670         if(this.jsonData){
25671             var p = property;
25672             var dsc = dir && dir.toLowerCase() == "desc";
25673             var f = function(o1, o2){
25674                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25675                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25676                 ;
25677                 if(v1 < v2){
25678                     return dsc ? +1 : -1;
25679                 } else if(v1 > v2){
25680                     return dsc ? -1 : +1;
25681                 } else{
25682                     return 0;
25683                 }
25684             };
25685             this.jsonData.sort(f);
25686             this.refresh();
25687             if(this.jsonData != this.snapshot){
25688                 this.snapshot.sort(f);
25689             }
25690         }
25691     }
25692 });/*
25693  * Based on:
25694  * Ext JS Library 1.1.1
25695  * Copyright(c) 2006-2007, Ext JS, LLC.
25696  *
25697  * Originally Released Under LGPL - original licence link has changed is not relivant.
25698  *
25699  * Fork - LGPL
25700  * <script type="text/javascript">
25701  */
25702  
25703
25704 /**
25705  * @class Roo.ColorPalette
25706  * @extends Roo.Component
25707  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25708  * Here's an example of typical usage:
25709  * <pre><code>
25710 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25711 cp.render('my-div');
25712
25713 cp.on('select', function(palette, selColor){
25714     // do something with selColor
25715 });
25716 </code></pre>
25717  * @constructor
25718  * Create a new ColorPalette
25719  * @param {Object} config The config object
25720  */
25721 Roo.ColorPalette = function(config){
25722     Roo.ColorPalette.superclass.constructor.call(this, config);
25723     this.addEvents({
25724         /**
25725              * @event select
25726              * Fires when a color is selected
25727              * @param {ColorPalette} this
25728              * @param {String} color The 6-digit color hex code (without the # symbol)
25729              */
25730         select: true
25731     });
25732
25733     if(this.handler){
25734         this.on("select", this.handler, this.scope, true);
25735     }
25736 };
25737 Roo.extend(Roo.ColorPalette, Roo.Component, {
25738     /**
25739      * @cfg {String} itemCls
25740      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25741      */
25742     itemCls : "x-color-palette",
25743     /**
25744      * @cfg {String} value
25745      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25746      * the hex codes are case-sensitive.
25747      */
25748     value : null,
25749     clickEvent:'click',
25750     // private
25751     ctype: "Roo.ColorPalette",
25752
25753     /**
25754      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25755      */
25756     allowReselect : false,
25757
25758     /**
25759      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25760      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25761      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25762      * of colors with the width setting until the box is symmetrical.</p>
25763      * <p>You can override individual colors if needed:</p>
25764      * <pre><code>
25765 var cp = new Roo.ColorPalette();
25766 cp.colors[0] = "FF0000";  // change the first box to red
25767 </code></pre>
25768
25769 Or you can provide a custom array of your own for complete control:
25770 <pre><code>
25771 var cp = new Roo.ColorPalette();
25772 cp.colors = ["000000", "993300", "333300"];
25773 </code></pre>
25774      * @type Array
25775      */
25776     colors : [
25777         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25778         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25779         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25780         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25781         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25782     ],
25783
25784     // private
25785     onRender : function(container, position){
25786         var t = new Roo.MasterTemplate(
25787             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25788         );
25789         var c = this.colors;
25790         for(var i = 0, len = c.length; i < len; i++){
25791             t.add([c[i]]);
25792         }
25793         var el = document.createElement("div");
25794         el.className = this.itemCls;
25795         t.overwrite(el);
25796         container.dom.insertBefore(el, position);
25797         this.el = Roo.get(el);
25798         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25799         if(this.clickEvent != 'click'){
25800             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25801         }
25802     },
25803
25804     // private
25805     afterRender : function(){
25806         Roo.ColorPalette.superclass.afterRender.call(this);
25807         if(this.value){
25808             var s = this.value;
25809             this.value = null;
25810             this.select(s);
25811         }
25812     },
25813
25814     // private
25815     handleClick : function(e, t){
25816         e.preventDefault();
25817         if(!this.disabled){
25818             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25819             this.select(c.toUpperCase());
25820         }
25821     },
25822
25823     /**
25824      * Selects the specified color in the palette (fires the select event)
25825      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25826      */
25827     select : function(color){
25828         color = color.replace("#", "");
25829         if(color != this.value || this.allowReselect){
25830             var el = this.el;
25831             if(this.value){
25832                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25833             }
25834             el.child("a.color-"+color).addClass("x-color-palette-sel");
25835             this.value = color;
25836             this.fireEvent("select", this, color);
25837         }
25838     }
25839 });/*
25840  * Based on:
25841  * Ext JS Library 1.1.1
25842  * Copyright(c) 2006-2007, Ext JS, LLC.
25843  *
25844  * Originally Released Under LGPL - original licence link has changed is not relivant.
25845  *
25846  * Fork - LGPL
25847  * <script type="text/javascript">
25848  */
25849  
25850 /**
25851  * @class Roo.DatePicker
25852  * @extends Roo.Component
25853  * Simple date picker class.
25854  * @constructor
25855  * Create a new DatePicker
25856  * @param {Object} config The config object
25857  */
25858 Roo.DatePicker = function(config){
25859     Roo.DatePicker.superclass.constructor.call(this, config);
25860
25861     this.value = config && config.value ?
25862                  config.value.clearTime() : new Date().clearTime();
25863
25864     this.addEvents({
25865         /**
25866              * @event select
25867              * Fires when a date is selected
25868              * @param {DatePicker} this
25869              * @param {Date} date The selected date
25870              */
25871         'select': true,
25872         /**
25873              * @event monthchange
25874              * Fires when the displayed month changes 
25875              * @param {DatePicker} this
25876              * @param {Date} date The selected month
25877              */
25878         'monthchange': true
25879     });
25880
25881     if(this.handler){
25882         this.on("select", this.handler,  this.scope || this);
25883     }
25884     // build the disabledDatesRE
25885     if(!this.disabledDatesRE && this.disabledDates){
25886         var dd = this.disabledDates;
25887         var re = "(?:";
25888         for(var i = 0; i < dd.length; i++){
25889             re += dd[i];
25890             if(i != dd.length-1) re += "|";
25891         }
25892         this.disabledDatesRE = new RegExp(re + ")");
25893     }
25894 };
25895
25896 Roo.extend(Roo.DatePicker, Roo.Component, {
25897     /**
25898      * @cfg {String} todayText
25899      * The text to display on the button that selects the current date (defaults to "Today")
25900      */
25901     todayText : "Today",
25902     /**
25903      * @cfg {String} okText
25904      * The text to display on the ok button
25905      */
25906     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25907     /**
25908      * @cfg {String} cancelText
25909      * The text to display on the cancel button
25910      */
25911     cancelText : "Cancel",
25912     /**
25913      * @cfg {String} todayTip
25914      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25915      */
25916     todayTip : "{0} (Spacebar)",
25917     /**
25918      * @cfg {Date} minDate
25919      * Minimum allowable date (JavaScript date object, defaults to null)
25920      */
25921     minDate : null,
25922     /**
25923      * @cfg {Date} maxDate
25924      * Maximum allowable date (JavaScript date object, defaults to null)
25925      */
25926     maxDate : null,
25927     /**
25928      * @cfg {String} minText
25929      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25930      */
25931     minText : "This date is before the minimum date",
25932     /**
25933      * @cfg {String} maxText
25934      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25935      */
25936     maxText : "This date is after the maximum date",
25937     /**
25938      * @cfg {String} format
25939      * The default date format string which can be overriden for localization support.  The format must be
25940      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25941      */
25942     format : "m/d/y",
25943     /**
25944      * @cfg {Array} disabledDays
25945      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25946      */
25947     disabledDays : null,
25948     /**
25949      * @cfg {String} disabledDaysText
25950      * The tooltip to display when the date falls on a disabled day (defaults to "")
25951      */
25952     disabledDaysText : "",
25953     /**
25954      * @cfg {RegExp} disabledDatesRE
25955      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25956      */
25957     disabledDatesRE : null,
25958     /**
25959      * @cfg {String} disabledDatesText
25960      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25961      */
25962     disabledDatesText : "",
25963     /**
25964      * @cfg {Boolean} constrainToViewport
25965      * True to constrain the date picker to the viewport (defaults to true)
25966      */
25967     constrainToViewport : true,
25968     /**
25969      * @cfg {Array} monthNames
25970      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25971      */
25972     monthNames : Date.monthNames,
25973     /**
25974      * @cfg {Array} dayNames
25975      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25976      */
25977     dayNames : Date.dayNames,
25978     /**
25979      * @cfg {String} nextText
25980      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25981      */
25982     nextText: 'Next Month (Control+Right)',
25983     /**
25984      * @cfg {String} prevText
25985      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25986      */
25987     prevText: 'Previous Month (Control+Left)',
25988     /**
25989      * @cfg {String} monthYearText
25990      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25991      */
25992     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25993     /**
25994      * @cfg {Number} startDay
25995      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25996      */
25997     startDay : 0,
25998     /**
25999      * @cfg {Bool} showClear
26000      * Show a clear button (usefull for date form elements that can be blank.)
26001      */
26002     
26003     showClear: false,
26004     
26005     /**
26006      * Sets the value of the date field
26007      * @param {Date} value The date to set
26008      */
26009     setValue : function(value){
26010         var old = this.value;
26011         
26012         if (typeof(value) == 'string') {
26013          
26014             value = Date.parseDate(value, this.format);
26015         }
26016         if (!value) {
26017             value = new Date();
26018         }
26019         
26020         this.value = value.clearTime(true);
26021         if(this.el){
26022             this.update(this.value);
26023         }
26024     },
26025
26026     /**
26027      * Gets the current selected value of the date field
26028      * @return {Date} The selected date
26029      */
26030     getValue : function(){
26031         return this.value;
26032     },
26033
26034     // private
26035     focus : function(){
26036         if(this.el){
26037             this.update(this.activeDate);
26038         }
26039     },
26040
26041     // privateval
26042     onRender : function(container, position){
26043         
26044         var m = [
26045              '<table cellspacing="0">',
26046                 '<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>',
26047                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26048         var dn = this.dayNames;
26049         for(var i = 0; i < 7; i++){
26050             var d = this.startDay+i;
26051             if(d > 6){
26052                 d = d-7;
26053             }
26054             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26055         }
26056         m[m.length] = "</tr></thead><tbody><tr>";
26057         for(var i = 0; i < 42; i++) {
26058             if(i % 7 == 0 && i != 0){
26059                 m[m.length] = "</tr><tr>";
26060             }
26061             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26062         }
26063         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26064             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26065
26066         var el = document.createElement("div");
26067         el.className = "x-date-picker";
26068         el.innerHTML = m.join("");
26069
26070         container.dom.insertBefore(el, position);
26071
26072         this.el = Roo.get(el);
26073         this.eventEl = Roo.get(el.firstChild);
26074
26075         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26076             handler: this.showPrevMonth,
26077             scope: this,
26078             preventDefault:true,
26079             stopDefault:true
26080         });
26081
26082         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26083             handler: this.showNextMonth,
26084             scope: this,
26085             preventDefault:true,
26086             stopDefault:true
26087         });
26088
26089         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26090
26091         this.monthPicker = this.el.down('div.x-date-mp');
26092         this.monthPicker.enableDisplayMode('block');
26093         
26094         var kn = new Roo.KeyNav(this.eventEl, {
26095             "left" : function(e){
26096                 e.ctrlKey ?
26097                     this.showPrevMonth() :
26098                     this.update(this.activeDate.add("d", -1));
26099             },
26100
26101             "right" : function(e){
26102                 e.ctrlKey ?
26103                     this.showNextMonth() :
26104                     this.update(this.activeDate.add("d", 1));
26105             },
26106
26107             "up" : function(e){
26108                 e.ctrlKey ?
26109                     this.showNextYear() :
26110                     this.update(this.activeDate.add("d", -7));
26111             },
26112
26113             "down" : function(e){
26114                 e.ctrlKey ?
26115                     this.showPrevYear() :
26116                     this.update(this.activeDate.add("d", 7));
26117             },
26118
26119             "pageUp" : function(e){
26120                 this.showNextMonth();
26121             },
26122
26123             "pageDown" : function(e){
26124                 this.showPrevMonth();
26125             },
26126
26127             "enter" : function(e){
26128                 e.stopPropagation();
26129                 return true;
26130             },
26131
26132             scope : this
26133         });
26134
26135         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26136
26137         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26138
26139         this.el.unselectable();
26140         
26141         this.cells = this.el.select("table.x-date-inner tbody td");
26142         this.textNodes = this.el.query("table.x-date-inner tbody span");
26143
26144         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26145             text: "&#160;",
26146             tooltip: this.monthYearText
26147         });
26148
26149         this.mbtn.on('click', this.showMonthPicker, this);
26150         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26151
26152
26153         var today = (new Date()).dateFormat(this.format);
26154         
26155         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26156         if (this.showClear) {
26157             baseTb.add( new Roo.Toolbar.Fill());
26158         }
26159         baseTb.add({
26160             text: String.format(this.todayText, today),
26161             tooltip: String.format(this.todayTip, today),
26162             handler: this.selectToday,
26163             scope: this
26164         });
26165         
26166         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26167             
26168         //});
26169         if (this.showClear) {
26170             
26171             baseTb.add( new Roo.Toolbar.Fill());
26172             baseTb.add({
26173                 text: '&#160;',
26174                 cls: 'x-btn-icon x-btn-clear',
26175                 handler: function() {
26176                     //this.value = '';
26177                     this.fireEvent("select", this, '');
26178                 },
26179                 scope: this
26180             });
26181         }
26182         
26183         
26184         if(Roo.isIE){
26185             this.el.repaint();
26186         }
26187         this.update(this.value);
26188     },
26189
26190     createMonthPicker : function(){
26191         if(!this.monthPicker.dom.firstChild){
26192             var buf = ['<table border="0" cellspacing="0">'];
26193             for(var i = 0; i < 6; i++){
26194                 buf.push(
26195                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26196                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26197                     i == 0 ?
26198                     '<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>' :
26199                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26200                 );
26201             }
26202             buf.push(
26203                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26204                     this.okText,
26205                     '</button><button type="button" class="x-date-mp-cancel">',
26206                     this.cancelText,
26207                     '</button></td></tr>',
26208                 '</table>'
26209             );
26210             this.monthPicker.update(buf.join(''));
26211             this.monthPicker.on('click', this.onMonthClick, this);
26212             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26213
26214             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26215             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26216
26217             this.mpMonths.each(function(m, a, i){
26218                 i += 1;
26219                 if((i%2) == 0){
26220                     m.dom.xmonth = 5 + Math.round(i * .5);
26221                 }else{
26222                     m.dom.xmonth = Math.round((i-1) * .5);
26223                 }
26224             });
26225         }
26226     },
26227
26228     showMonthPicker : function(){
26229         this.createMonthPicker();
26230         var size = this.el.getSize();
26231         this.monthPicker.setSize(size);
26232         this.monthPicker.child('table').setSize(size);
26233
26234         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26235         this.updateMPMonth(this.mpSelMonth);
26236         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26237         this.updateMPYear(this.mpSelYear);
26238
26239         this.monthPicker.slideIn('t', {duration:.2});
26240     },
26241
26242     updateMPYear : function(y){
26243         this.mpyear = y;
26244         var ys = this.mpYears.elements;
26245         for(var i = 1; i <= 10; i++){
26246             var td = ys[i-1], y2;
26247             if((i%2) == 0){
26248                 y2 = y + Math.round(i * .5);
26249                 td.firstChild.innerHTML = y2;
26250                 td.xyear = y2;
26251             }else{
26252                 y2 = y - (5-Math.round(i * .5));
26253                 td.firstChild.innerHTML = y2;
26254                 td.xyear = y2;
26255             }
26256             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26257         }
26258     },
26259
26260     updateMPMonth : function(sm){
26261         this.mpMonths.each(function(m, a, i){
26262             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26263         });
26264     },
26265
26266     selectMPMonth: function(m){
26267         
26268     },
26269
26270     onMonthClick : function(e, t){
26271         e.stopEvent();
26272         var el = new Roo.Element(t), pn;
26273         if(el.is('button.x-date-mp-cancel')){
26274             this.hideMonthPicker();
26275         }
26276         else if(el.is('button.x-date-mp-ok')){
26277             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26278             this.hideMonthPicker();
26279         }
26280         else if(pn = el.up('td.x-date-mp-month', 2)){
26281             this.mpMonths.removeClass('x-date-mp-sel');
26282             pn.addClass('x-date-mp-sel');
26283             this.mpSelMonth = pn.dom.xmonth;
26284         }
26285         else if(pn = el.up('td.x-date-mp-year', 2)){
26286             this.mpYears.removeClass('x-date-mp-sel');
26287             pn.addClass('x-date-mp-sel');
26288             this.mpSelYear = pn.dom.xyear;
26289         }
26290         else if(el.is('a.x-date-mp-prev')){
26291             this.updateMPYear(this.mpyear-10);
26292         }
26293         else if(el.is('a.x-date-mp-next')){
26294             this.updateMPYear(this.mpyear+10);
26295         }
26296     },
26297
26298     onMonthDblClick : function(e, t){
26299         e.stopEvent();
26300         var el = new Roo.Element(t), pn;
26301         if(pn = el.up('td.x-date-mp-month', 2)){
26302             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26303             this.hideMonthPicker();
26304         }
26305         else if(pn = el.up('td.x-date-mp-year', 2)){
26306             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26307             this.hideMonthPicker();
26308         }
26309     },
26310
26311     hideMonthPicker : function(disableAnim){
26312         if(this.monthPicker){
26313             if(disableAnim === true){
26314                 this.monthPicker.hide();
26315             }else{
26316                 this.monthPicker.slideOut('t', {duration:.2});
26317             }
26318         }
26319     },
26320
26321     // private
26322     showPrevMonth : function(e){
26323         this.update(this.activeDate.add("mo", -1));
26324     },
26325
26326     // private
26327     showNextMonth : function(e){
26328         this.update(this.activeDate.add("mo", 1));
26329     },
26330
26331     // private
26332     showPrevYear : function(){
26333         this.update(this.activeDate.add("y", -1));
26334     },
26335
26336     // private
26337     showNextYear : function(){
26338         this.update(this.activeDate.add("y", 1));
26339     },
26340
26341     // private
26342     handleMouseWheel : function(e){
26343         var delta = e.getWheelDelta();
26344         if(delta > 0){
26345             this.showPrevMonth();
26346             e.stopEvent();
26347         } else if(delta < 0){
26348             this.showNextMonth();
26349             e.stopEvent();
26350         }
26351     },
26352
26353     // private
26354     handleDateClick : function(e, t){
26355         e.stopEvent();
26356         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26357             this.setValue(new Date(t.dateValue));
26358             this.fireEvent("select", this, this.value);
26359         }
26360     },
26361
26362     // private
26363     selectToday : function(){
26364         this.setValue(new Date().clearTime());
26365         this.fireEvent("select", this, this.value);
26366     },
26367
26368     // private
26369     update : function(date)
26370     {
26371         var vd = this.activeDate;
26372         this.activeDate = date;
26373         if(vd && this.el){
26374             var t = date.getTime();
26375             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26376                 this.cells.removeClass("x-date-selected");
26377                 this.cells.each(function(c){
26378                    if(c.dom.firstChild.dateValue == t){
26379                        c.addClass("x-date-selected");
26380                        setTimeout(function(){
26381                             try{c.dom.firstChild.focus();}catch(e){}
26382                        }, 50);
26383                        return false;
26384                    }
26385                 });
26386                 return;
26387             }
26388         }
26389         
26390         var days = date.getDaysInMonth();
26391         var firstOfMonth = date.getFirstDateOfMonth();
26392         var startingPos = firstOfMonth.getDay()-this.startDay;
26393
26394         if(startingPos <= this.startDay){
26395             startingPos += 7;
26396         }
26397
26398         var pm = date.add("mo", -1);
26399         var prevStart = pm.getDaysInMonth()-startingPos;
26400
26401         var cells = this.cells.elements;
26402         var textEls = this.textNodes;
26403         days += startingPos;
26404
26405         // convert everything to numbers so it's fast
26406         var day = 86400000;
26407         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26408         var today = new Date().clearTime().getTime();
26409         var sel = date.clearTime().getTime();
26410         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26411         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26412         var ddMatch = this.disabledDatesRE;
26413         var ddText = this.disabledDatesText;
26414         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26415         var ddaysText = this.disabledDaysText;
26416         var format = this.format;
26417
26418         var setCellClass = function(cal, cell){
26419             cell.title = "";
26420             var t = d.getTime();
26421             cell.firstChild.dateValue = t;
26422             if(t == today){
26423                 cell.className += " x-date-today";
26424                 cell.title = cal.todayText;
26425             }
26426             if(t == sel){
26427                 cell.className += " x-date-selected";
26428                 setTimeout(function(){
26429                     try{cell.firstChild.focus();}catch(e){}
26430                 }, 50);
26431             }
26432             // disabling
26433             if(t < min) {
26434                 cell.className = " x-date-disabled";
26435                 cell.title = cal.minText;
26436                 return;
26437             }
26438             if(t > max) {
26439                 cell.className = " x-date-disabled";
26440                 cell.title = cal.maxText;
26441                 return;
26442             }
26443             if(ddays){
26444                 if(ddays.indexOf(d.getDay()) != -1){
26445                     cell.title = ddaysText;
26446                     cell.className = " x-date-disabled";
26447                 }
26448             }
26449             if(ddMatch && format){
26450                 var fvalue = d.dateFormat(format);
26451                 if(ddMatch.test(fvalue)){
26452                     cell.title = ddText.replace("%0", fvalue);
26453                     cell.className = " x-date-disabled";
26454                 }
26455             }
26456         };
26457
26458         var i = 0;
26459         for(; i < startingPos; i++) {
26460             textEls[i].innerHTML = (++prevStart);
26461             d.setDate(d.getDate()+1);
26462             cells[i].className = "x-date-prevday";
26463             setCellClass(this, cells[i]);
26464         }
26465         for(; i < days; i++){
26466             intDay = i - startingPos + 1;
26467             textEls[i].innerHTML = (intDay);
26468             d.setDate(d.getDate()+1);
26469             cells[i].className = "x-date-active";
26470             setCellClass(this, cells[i]);
26471         }
26472         var extraDays = 0;
26473         for(; i < 42; i++) {
26474              textEls[i].innerHTML = (++extraDays);
26475              d.setDate(d.getDate()+1);
26476              cells[i].className = "x-date-nextday";
26477              setCellClass(this, cells[i]);
26478         }
26479
26480         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26481         this.fireEvent('monthchange', this, date);
26482         
26483         if(!this.internalRender){
26484             var main = this.el.dom.firstChild;
26485             var w = main.offsetWidth;
26486             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26487             Roo.fly(main).setWidth(w);
26488             this.internalRender = true;
26489             // opera does not respect the auto grow header center column
26490             // then, after it gets a width opera refuses to recalculate
26491             // without a second pass
26492             if(Roo.isOpera && !this.secondPass){
26493                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26494                 this.secondPass = true;
26495                 this.update.defer(10, this, [date]);
26496             }
26497         }
26498         
26499         
26500     }
26501 });        /*
26502  * Based on:
26503  * Ext JS Library 1.1.1
26504  * Copyright(c) 2006-2007, Ext JS, LLC.
26505  *
26506  * Originally Released Under LGPL - original licence link has changed is not relivant.
26507  *
26508  * Fork - LGPL
26509  * <script type="text/javascript">
26510  */
26511 /**
26512  * @class Roo.TabPanel
26513  * @extends Roo.util.Observable
26514  * A lightweight tab container.
26515  * <br><br>
26516  * Usage:
26517  * <pre><code>
26518 // basic tabs 1, built from existing content
26519 var tabs = new Roo.TabPanel("tabs1");
26520 tabs.addTab("script", "View Script");
26521 tabs.addTab("markup", "View Markup");
26522 tabs.activate("script");
26523
26524 // more advanced tabs, built from javascript
26525 var jtabs = new Roo.TabPanel("jtabs");
26526 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26527
26528 // set up the UpdateManager
26529 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26530 var updater = tab2.getUpdateManager();
26531 updater.setDefaultUrl("ajax1.htm");
26532 tab2.on('activate', updater.refresh, updater, true);
26533
26534 // Use setUrl for Ajax loading
26535 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26536 tab3.setUrl("ajax2.htm", null, true);
26537
26538 // Disabled tab
26539 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26540 tab4.disable();
26541
26542 jtabs.activate("jtabs-1");
26543  * </code></pre>
26544  * @constructor
26545  * Create a new TabPanel.
26546  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26547  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26548  */
26549 Roo.TabPanel = function(container, config){
26550     /**
26551     * The container element for this TabPanel.
26552     * @type Roo.Element
26553     */
26554     this.el = Roo.get(container, true);
26555     if(config){
26556         if(typeof config == "boolean"){
26557             this.tabPosition = config ? "bottom" : "top";
26558         }else{
26559             Roo.apply(this, config);
26560         }
26561     }
26562     if(this.tabPosition == "bottom"){
26563         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26564         this.el.addClass("x-tabs-bottom");
26565     }
26566     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26567     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26568     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26569     if(Roo.isIE){
26570         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26571     }
26572     if(this.tabPosition != "bottom"){
26573         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26574          * @type Roo.Element
26575          */
26576         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26577         this.el.addClass("x-tabs-top");
26578     }
26579     this.items = [];
26580
26581     this.bodyEl.setStyle("position", "relative");
26582
26583     this.active = null;
26584     this.activateDelegate = this.activate.createDelegate(this);
26585
26586     this.addEvents({
26587         /**
26588          * @event tabchange
26589          * Fires when the active tab changes
26590          * @param {Roo.TabPanel} this
26591          * @param {Roo.TabPanelItem} activePanel The new active tab
26592          */
26593         "tabchange": true,
26594         /**
26595          * @event beforetabchange
26596          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26597          * @param {Roo.TabPanel} this
26598          * @param {Object} e Set cancel to true on this object to cancel the tab change
26599          * @param {Roo.TabPanelItem} tab The tab being changed to
26600          */
26601         "beforetabchange" : true
26602     });
26603
26604     Roo.EventManager.onWindowResize(this.onResize, this);
26605     this.cpad = this.el.getPadding("lr");
26606     this.hiddenCount = 0;
26607
26608
26609     // toolbar on the tabbar support...
26610     if (this.toolbar) {
26611         var tcfg = this.toolbar;
26612         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26613         this.toolbar = new Roo.Toolbar(tcfg);
26614         if (Roo.isSafari) {
26615             var tbl = tcfg.container.child('table', true);
26616             tbl.setAttribute('width', '100%');
26617         }
26618         
26619     }
26620    
26621
26622
26623     Roo.TabPanel.superclass.constructor.call(this);
26624 };
26625
26626 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26627     /*
26628      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26629      */
26630     tabPosition : "top",
26631     /*
26632      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26633      */
26634     currentTabWidth : 0,
26635     /*
26636      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26637      */
26638     minTabWidth : 40,
26639     /*
26640      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26641      */
26642     maxTabWidth : 250,
26643     /*
26644      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26645      */
26646     preferredTabWidth : 175,
26647     /*
26648      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26649      */
26650     resizeTabs : false,
26651     /*
26652      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26653      */
26654     monitorResize : true,
26655     /*
26656      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26657      */
26658     toolbar : false,
26659
26660     /**
26661      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26662      * @param {String} id The id of the div to use <b>or create</b>
26663      * @param {String} text The text for the tab
26664      * @param {String} content (optional) Content to put in the TabPanelItem body
26665      * @param {Boolean} closable (optional) True to create a close icon on the tab
26666      * @return {Roo.TabPanelItem} The created TabPanelItem
26667      */
26668     addTab : function(id, text, content, closable){
26669         var item = new Roo.TabPanelItem(this, id, text, closable);
26670         this.addTabItem(item);
26671         if(content){
26672             item.setContent(content);
26673         }
26674         return item;
26675     },
26676
26677     /**
26678      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26679      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26680      * @return {Roo.TabPanelItem}
26681      */
26682     getTab : function(id){
26683         return this.items[id];
26684     },
26685
26686     /**
26687      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26688      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26689      */
26690     hideTab : function(id){
26691         var t = this.items[id];
26692         if(!t.isHidden()){
26693            t.setHidden(true);
26694            this.hiddenCount++;
26695            this.autoSizeTabs();
26696         }
26697     },
26698
26699     /**
26700      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26701      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26702      */
26703     unhideTab : function(id){
26704         var t = this.items[id];
26705         if(t.isHidden()){
26706            t.setHidden(false);
26707            this.hiddenCount--;
26708            this.autoSizeTabs();
26709         }
26710     },
26711
26712     /**
26713      * Adds an existing {@link Roo.TabPanelItem}.
26714      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26715      */
26716     addTabItem : function(item){
26717         this.items[item.id] = item;
26718         this.items.push(item);
26719         if(this.resizeTabs){
26720            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26721            this.autoSizeTabs();
26722         }else{
26723             item.autoSize();
26724         }
26725     },
26726
26727     /**
26728      * Removes a {@link Roo.TabPanelItem}.
26729      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26730      */
26731     removeTab : function(id){
26732         var items = this.items;
26733         var tab = items[id];
26734         if(!tab) { return; }
26735         var index = items.indexOf(tab);
26736         if(this.active == tab && items.length > 1){
26737             var newTab = this.getNextAvailable(index);
26738             if(newTab) {
26739                 newTab.activate();
26740             }
26741         }
26742         this.stripEl.dom.removeChild(tab.pnode.dom);
26743         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26744             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26745         }
26746         items.splice(index, 1);
26747         delete this.items[tab.id];
26748         tab.fireEvent("close", tab);
26749         tab.purgeListeners();
26750         this.autoSizeTabs();
26751     },
26752
26753     getNextAvailable : function(start){
26754         var items = this.items;
26755         var index = start;
26756         // look for a next tab that will slide over to
26757         // replace the one being removed
26758         while(index < items.length){
26759             var item = items[++index];
26760             if(item && !item.isHidden()){
26761                 return item;
26762             }
26763         }
26764         // if one isn't found select the previous tab (on the left)
26765         index = start;
26766         while(index >= 0){
26767             var item = items[--index];
26768             if(item && !item.isHidden()){
26769                 return item;
26770             }
26771         }
26772         return null;
26773     },
26774
26775     /**
26776      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26777      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26778      */
26779     disableTab : function(id){
26780         var tab = this.items[id];
26781         if(tab && this.active != tab){
26782             tab.disable();
26783         }
26784     },
26785
26786     /**
26787      * Enables a {@link Roo.TabPanelItem} that is disabled.
26788      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26789      */
26790     enableTab : function(id){
26791         var tab = this.items[id];
26792         tab.enable();
26793     },
26794
26795     /**
26796      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26797      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26798      * @return {Roo.TabPanelItem} The TabPanelItem.
26799      */
26800     activate : function(id){
26801         var tab = this.items[id];
26802         if(!tab){
26803             return null;
26804         }
26805         if(tab == this.active || tab.disabled){
26806             return tab;
26807         }
26808         var e = {};
26809         this.fireEvent("beforetabchange", this, e, tab);
26810         if(e.cancel !== true && !tab.disabled){
26811             if(this.active){
26812                 this.active.hide();
26813             }
26814             this.active = this.items[id];
26815             this.active.show();
26816             this.fireEvent("tabchange", this, this.active);
26817         }
26818         return tab;
26819     },
26820
26821     /**
26822      * Gets the active {@link Roo.TabPanelItem}.
26823      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26824      */
26825     getActiveTab : function(){
26826         return this.active;
26827     },
26828
26829     /**
26830      * Updates the tab body element to fit the height of the container element
26831      * for overflow scrolling
26832      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26833      */
26834     syncHeight : function(targetHeight){
26835         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26836         var bm = this.bodyEl.getMargins();
26837         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26838         this.bodyEl.setHeight(newHeight);
26839         return newHeight;
26840     },
26841
26842     onResize : function(){
26843         if(this.monitorResize){
26844             this.autoSizeTabs();
26845         }
26846     },
26847
26848     /**
26849      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26850      */
26851     beginUpdate : function(){
26852         this.updating = true;
26853     },
26854
26855     /**
26856      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26857      */
26858     endUpdate : function(){
26859         this.updating = false;
26860         this.autoSizeTabs();
26861     },
26862
26863     /**
26864      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26865      */
26866     autoSizeTabs : function(){
26867         var count = this.items.length;
26868         var vcount = count - this.hiddenCount;
26869         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26870         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26871         var availWidth = Math.floor(w / vcount);
26872         var b = this.stripBody;
26873         if(b.getWidth() > w){
26874             var tabs = this.items;
26875             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26876             if(availWidth < this.minTabWidth){
26877                 /*if(!this.sleft){    // incomplete scrolling code
26878                     this.createScrollButtons();
26879                 }
26880                 this.showScroll();
26881                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26882             }
26883         }else{
26884             if(this.currentTabWidth < this.preferredTabWidth){
26885                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26886             }
26887         }
26888     },
26889
26890     /**
26891      * Returns the number of tabs in this TabPanel.
26892      * @return {Number}
26893      */
26894      getCount : function(){
26895          return this.items.length;
26896      },
26897
26898     /**
26899      * Resizes all the tabs to the passed width
26900      * @param {Number} The new width
26901      */
26902     setTabWidth : function(width){
26903         this.currentTabWidth = width;
26904         for(var i = 0, len = this.items.length; i < len; i++) {
26905                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26906         }
26907     },
26908
26909     /**
26910      * Destroys this TabPanel
26911      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26912      */
26913     destroy : function(removeEl){
26914         Roo.EventManager.removeResizeListener(this.onResize, this);
26915         for(var i = 0, len = this.items.length; i < len; i++){
26916             this.items[i].purgeListeners();
26917         }
26918         if(removeEl === true){
26919             this.el.update("");
26920             this.el.remove();
26921         }
26922     }
26923 });
26924
26925 /**
26926  * @class Roo.TabPanelItem
26927  * @extends Roo.util.Observable
26928  * Represents an individual item (tab plus body) in a TabPanel.
26929  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26930  * @param {String} id The id of this TabPanelItem
26931  * @param {String} text The text for the tab of this TabPanelItem
26932  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26933  */
26934 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26935     /**
26936      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26937      * @type Roo.TabPanel
26938      */
26939     this.tabPanel = tabPanel;
26940     /**
26941      * The id for this TabPanelItem
26942      * @type String
26943      */
26944     this.id = id;
26945     /** @private */
26946     this.disabled = false;
26947     /** @private */
26948     this.text = text;
26949     /** @private */
26950     this.loaded = false;
26951     this.closable = closable;
26952
26953     /**
26954      * The body element for this TabPanelItem.
26955      * @type Roo.Element
26956      */
26957     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26958     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26959     this.bodyEl.setStyle("display", "block");
26960     this.bodyEl.setStyle("zoom", "1");
26961     this.hideAction();
26962
26963     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26964     /** @private */
26965     this.el = Roo.get(els.el, true);
26966     this.inner = Roo.get(els.inner, true);
26967     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26968     this.pnode = Roo.get(els.el.parentNode, true);
26969     this.el.on("mousedown", this.onTabMouseDown, this);
26970     this.el.on("click", this.onTabClick, this);
26971     /** @private */
26972     if(closable){
26973         var c = Roo.get(els.close, true);
26974         c.dom.title = this.closeText;
26975         c.addClassOnOver("close-over");
26976         c.on("click", this.closeClick, this);
26977      }
26978
26979     this.addEvents({
26980          /**
26981          * @event activate
26982          * Fires when this tab becomes the active tab.
26983          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26984          * @param {Roo.TabPanelItem} this
26985          */
26986         "activate": true,
26987         /**
26988          * @event beforeclose
26989          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26990          * @param {Roo.TabPanelItem} this
26991          * @param {Object} e Set cancel to true on this object to cancel the close.
26992          */
26993         "beforeclose": true,
26994         /**
26995          * @event close
26996          * Fires when this tab is closed.
26997          * @param {Roo.TabPanelItem} this
26998          */
26999          "close": true,
27000         /**
27001          * @event deactivate
27002          * Fires when this tab is no longer the active tab.
27003          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27004          * @param {Roo.TabPanelItem} this
27005          */
27006          "deactivate" : true
27007     });
27008     this.hidden = false;
27009
27010     Roo.TabPanelItem.superclass.constructor.call(this);
27011 };
27012
27013 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27014     purgeListeners : function(){
27015        Roo.util.Observable.prototype.purgeListeners.call(this);
27016        this.el.removeAllListeners();
27017     },
27018     /**
27019      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27020      */
27021     show : function(){
27022         this.pnode.addClass("on");
27023         this.showAction();
27024         if(Roo.isOpera){
27025             this.tabPanel.stripWrap.repaint();
27026         }
27027         this.fireEvent("activate", this.tabPanel, this);
27028     },
27029
27030     /**
27031      * Returns true if this tab is the active tab.
27032      * @return {Boolean}
27033      */
27034     isActive : function(){
27035         return this.tabPanel.getActiveTab() == this;
27036     },
27037
27038     /**
27039      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27040      */
27041     hide : function(){
27042         this.pnode.removeClass("on");
27043         this.hideAction();
27044         this.fireEvent("deactivate", this.tabPanel, this);
27045     },
27046
27047     hideAction : function(){
27048         this.bodyEl.hide();
27049         this.bodyEl.setStyle("position", "absolute");
27050         this.bodyEl.setLeft("-20000px");
27051         this.bodyEl.setTop("-20000px");
27052     },
27053
27054     showAction : function(){
27055         this.bodyEl.setStyle("position", "relative");
27056         this.bodyEl.setTop("");
27057         this.bodyEl.setLeft("");
27058         this.bodyEl.show();
27059     },
27060
27061     /**
27062      * Set the tooltip for the tab.
27063      * @param {String} tooltip The tab's tooltip
27064      */
27065     setTooltip : function(text){
27066         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27067             this.textEl.dom.qtip = text;
27068             this.textEl.dom.removeAttribute('title');
27069         }else{
27070             this.textEl.dom.title = text;
27071         }
27072     },
27073
27074     onTabClick : function(e){
27075         e.preventDefault();
27076         this.tabPanel.activate(this.id);
27077     },
27078
27079     onTabMouseDown : function(e){
27080         e.preventDefault();
27081         this.tabPanel.activate(this.id);
27082     },
27083
27084     getWidth : function(){
27085         return this.inner.getWidth();
27086     },
27087
27088     setWidth : function(width){
27089         var iwidth = width - this.pnode.getPadding("lr");
27090         this.inner.setWidth(iwidth);
27091         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27092         this.pnode.setWidth(width);
27093     },
27094
27095     /**
27096      * Show or hide the tab
27097      * @param {Boolean} hidden True to hide or false to show.
27098      */
27099     setHidden : function(hidden){
27100         this.hidden = hidden;
27101         this.pnode.setStyle("display", hidden ? "none" : "");
27102     },
27103
27104     /**
27105      * Returns true if this tab is "hidden"
27106      * @return {Boolean}
27107      */
27108     isHidden : function(){
27109         return this.hidden;
27110     },
27111
27112     /**
27113      * Returns the text for this tab
27114      * @return {String}
27115      */
27116     getText : function(){
27117         return this.text;
27118     },
27119
27120     autoSize : function(){
27121         //this.el.beginMeasure();
27122         this.textEl.setWidth(1);
27123         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27124         //this.el.endMeasure();
27125     },
27126
27127     /**
27128      * Sets the text for the tab (Note: this also sets the tooltip text)
27129      * @param {String} text The tab's text and tooltip
27130      */
27131     setText : function(text){
27132         this.text = text;
27133         this.textEl.update(text);
27134         this.setTooltip(text);
27135         if(!this.tabPanel.resizeTabs){
27136             this.autoSize();
27137         }
27138     },
27139     /**
27140      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27141      */
27142     activate : function(){
27143         this.tabPanel.activate(this.id);
27144     },
27145
27146     /**
27147      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27148      */
27149     disable : function(){
27150         if(this.tabPanel.active != this){
27151             this.disabled = true;
27152             this.pnode.addClass("disabled");
27153         }
27154     },
27155
27156     /**
27157      * Enables this TabPanelItem if it was previously disabled.
27158      */
27159     enable : function(){
27160         this.disabled = false;
27161         this.pnode.removeClass("disabled");
27162     },
27163
27164     /**
27165      * Sets the content for this TabPanelItem.
27166      * @param {String} content The content
27167      * @param {Boolean} loadScripts true to look for and load scripts
27168      */
27169     setContent : function(content, loadScripts){
27170         this.bodyEl.update(content, loadScripts);
27171     },
27172
27173     /**
27174      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27175      * @return {Roo.UpdateManager} The UpdateManager
27176      */
27177     getUpdateManager : function(){
27178         return this.bodyEl.getUpdateManager();
27179     },
27180
27181     /**
27182      * Set a URL to be used to load the content for this TabPanelItem.
27183      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27184      * @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)
27185      * @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)
27186      * @return {Roo.UpdateManager} The UpdateManager
27187      */
27188     setUrl : function(url, params, loadOnce){
27189         if(this.refreshDelegate){
27190             this.un('activate', this.refreshDelegate);
27191         }
27192         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27193         this.on("activate", this.refreshDelegate);
27194         return this.bodyEl.getUpdateManager();
27195     },
27196
27197     /** @private */
27198     _handleRefresh : function(url, params, loadOnce){
27199         if(!loadOnce || !this.loaded){
27200             var updater = this.bodyEl.getUpdateManager();
27201             updater.update(url, params, this._setLoaded.createDelegate(this));
27202         }
27203     },
27204
27205     /**
27206      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27207      *   Will fail silently if the setUrl method has not been called.
27208      *   This does not activate the panel, just updates its content.
27209      */
27210     refresh : function(){
27211         if(this.refreshDelegate){
27212            this.loaded = false;
27213            this.refreshDelegate();
27214         }
27215     },
27216
27217     /** @private */
27218     _setLoaded : function(){
27219         this.loaded = true;
27220     },
27221
27222     /** @private */
27223     closeClick : function(e){
27224         var o = {};
27225         e.stopEvent();
27226         this.fireEvent("beforeclose", this, o);
27227         if(o.cancel !== true){
27228             this.tabPanel.removeTab(this.id);
27229         }
27230     },
27231     /**
27232      * The text displayed in the tooltip for the close icon.
27233      * @type String
27234      */
27235     closeText : "Close this tab"
27236 });
27237
27238 /** @private */
27239 Roo.TabPanel.prototype.createStrip = function(container){
27240     var strip = document.createElement("div");
27241     strip.className = "x-tabs-wrap";
27242     container.appendChild(strip);
27243     return strip;
27244 };
27245 /** @private */
27246 Roo.TabPanel.prototype.createStripList = function(strip){
27247     // div wrapper for retard IE
27248     // returns the "tr" element.
27249     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27250         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27251         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27252     return strip.firstChild.firstChild.firstChild.firstChild;
27253 };
27254 /** @private */
27255 Roo.TabPanel.prototype.createBody = function(container){
27256     var body = document.createElement("div");
27257     Roo.id(body, "tab-body");
27258     Roo.fly(body).addClass("x-tabs-body");
27259     container.appendChild(body);
27260     return body;
27261 };
27262 /** @private */
27263 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27264     var body = Roo.getDom(id);
27265     if(!body){
27266         body = document.createElement("div");
27267         body.id = id;
27268     }
27269     Roo.fly(body).addClass("x-tabs-item-body");
27270     bodyEl.insertBefore(body, bodyEl.firstChild);
27271     return body;
27272 };
27273 /** @private */
27274 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27275     var td = document.createElement("td");
27276     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27277     //stripEl.appendChild(td);
27278     if(closable){
27279         td.className = "x-tabs-closable";
27280         if(!this.closeTpl){
27281             this.closeTpl = new Roo.Template(
27282                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27283                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27284                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27285             );
27286         }
27287         var el = this.closeTpl.overwrite(td, {"text": text});
27288         var close = el.getElementsByTagName("div")[0];
27289         var inner = el.getElementsByTagName("em")[0];
27290         return {"el": el, "close": close, "inner": inner};
27291     } else {
27292         if(!this.tabTpl){
27293             this.tabTpl = new Roo.Template(
27294                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27295                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27296             );
27297         }
27298         var el = this.tabTpl.overwrite(td, {"text": text});
27299         var inner = el.getElementsByTagName("em")[0];
27300         return {"el": el, "inner": inner};
27301     }
27302 };/*
27303  * Based on:
27304  * Ext JS Library 1.1.1
27305  * Copyright(c) 2006-2007, Ext JS, LLC.
27306  *
27307  * Originally Released Under LGPL - original licence link has changed is not relivant.
27308  *
27309  * Fork - LGPL
27310  * <script type="text/javascript">
27311  */
27312
27313 /**
27314  * @class Roo.Button
27315  * @extends Roo.util.Observable
27316  * Simple Button class
27317  * @cfg {String} text The button text
27318  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27319  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27320  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27321  * @cfg {Object} scope The scope of the handler
27322  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27323  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27324  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27325  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27326  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27327  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27328    applies if enableToggle = true)
27329  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27330  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27331   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27332  * @constructor
27333  * Create a new button
27334  * @param {Object} config The config object
27335  */
27336 Roo.Button = function(renderTo, config)
27337 {
27338     if (!config) {
27339         config = renderTo;
27340         renderTo = config.renderTo || false;
27341     }
27342     
27343     Roo.apply(this, config);
27344     this.addEvents({
27345         /**
27346              * @event click
27347              * Fires when this button is clicked
27348              * @param {Button} this
27349              * @param {EventObject} e The click event
27350              */
27351             "click" : true,
27352         /**
27353              * @event toggle
27354              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27355              * @param {Button} this
27356              * @param {Boolean} pressed
27357              */
27358             "toggle" : true,
27359         /**
27360              * @event mouseover
27361              * Fires when the mouse hovers over the button
27362              * @param {Button} this
27363              * @param {Event} e The event object
27364              */
27365         'mouseover' : true,
27366         /**
27367              * @event mouseout
27368              * Fires when the mouse exits the button
27369              * @param {Button} this
27370              * @param {Event} e The event object
27371              */
27372         'mouseout': true,
27373          /**
27374              * @event render
27375              * Fires when the button is rendered
27376              * @param {Button} this
27377              */
27378         'render': true
27379     });
27380     if(this.menu){
27381         this.menu = Roo.menu.MenuMgr.get(this.menu);
27382     }
27383     // register listeners first!!  - so render can be captured..
27384     Roo.util.Observable.call(this);
27385     if(renderTo){
27386         this.render(renderTo);
27387     }
27388     
27389   
27390 };
27391
27392 Roo.extend(Roo.Button, Roo.util.Observable, {
27393     /**
27394      * 
27395      */
27396     
27397     /**
27398      * Read-only. True if this button is hidden
27399      * @type Boolean
27400      */
27401     hidden : false,
27402     /**
27403      * Read-only. True if this button is disabled
27404      * @type Boolean
27405      */
27406     disabled : false,
27407     /**
27408      * Read-only. True if this button is pressed (only if enableToggle = true)
27409      * @type Boolean
27410      */
27411     pressed : false,
27412
27413     /**
27414      * @cfg {Number} tabIndex 
27415      * The DOM tabIndex for this button (defaults to undefined)
27416      */
27417     tabIndex : undefined,
27418
27419     /**
27420      * @cfg {Boolean} enableToggle
27421      * True to enable pressed/not pressed toggling (defaults to false)
27422      */
27423     enableToggle: false,
27424     /**
27425      * @cfg {Mixed} menu
27426      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27427      */
27428     menu : undefined,
27429     /**
27430      * @cfg {String} menuAlign
27431      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27432      */
27433     menuAlign : "tl-bl?",
27434
27435     /**
27436      * @cfg {String} iconCls
27437      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27438      */
27439     iconCls : undefined,
27440     /**
27441      * @cfg {String} type
27442      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27443      */
27444     type : 'button',
27445
27446     // private
27447     menuClassTarget: 'tr',
27448
27449     /**
27450      * @cfg {String} clickEvent
27451      * The type of event to map to the button's event handler (defaults to 'click')
27452      */
27453     clickEvent : 'click',
27454
27455     /**
27456      * @cfg {Boolean} handleMouseEvents
27457      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27458      */
27459     handleMouseEvents : true,
27460
27461     /**
27462      * @cfg {String} tooltipType
27463      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27464      */
27465     tooltipType : 'qtip',
27466
27467     /**
27468      * @cfg {String} cls
27469      * A CSS class to apply to the button's main element.
27470      */
27471     
27472     /**
27473      * @cfg {Roo.Template} template (Optional)
27474      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27475      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27476      * require code modifications if required elements (e.g. a button) aren't present.
27477      */
27478
27479     // private
27480     render : function(renderTo){
27481         var btn;
27482         if(this.hideParent){
27483             this.parentEl = Roo.get(renderTo);
27484         }
27485         if(!this.dhconfig){
27486             if(!this.template){
27487                 if(!Roo.Button.buttonTemplate){
27488                     // hideous table template
27489                     Roo.Button.buttonTemplate = new Roo.Template(
27490                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27491                         '<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>',
27492                         "</tr></tbody></table>");
27493                 }
27494                 this.template = Roo.Button.buttonTemplate;
27495             }
27496             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27497             var btnEl = btn.child("button:first");
27498             btnEl.on('focus', this.onFocus, this);
27499             btnEl.on('blur', this.onBlur, this);
27500             if(this.cls){
27501                 btn.addClass(this.cls);
27502             }
27503             if(this.icon){
27504                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27505             }
27506             if(this.iconCls){
27507                 btnEl.addClass(this.iconCls);
27508                 if(!this.cls){
27509                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27510                 }
27511             }
27512             if(this.tabIndex !== undefined){
27513                 btnEl.dom.tabIndex = this.tabIndex;
27514             }
27515             if(this.tooltip){
27516                 if(typeof this.tooltip == 'object'){
27517                     Roo.QuickTips.tips(Roo.apply({
27518                           target: btnEl.id
27519                     }, this.tooltip));
27520                 } else {
27521                     btnEl.dom[this.tooltipType] = this.tooltip;
27522                 }
27523             }
27524         }else{
27525             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27526         }
27527         this.el = btn;
27528         if(this.id){
27529             this.el.dom.id = this.el.id = this.id;
27530         }
27531         if(this.menu){
27532             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27533             this.menu.on("show", this.onMenuShow, this);
27534             this.menu.on("hide", this.onMenuHide, this);
27535         }
27536         btn.addClass("x-btn");
27537         if(Roo.isIE && !Roo.isIE7){
27538             this.autoWidth.defer(1, this);
27539         }else{
27540             this.autoWidth();
27541         }
27542         if(this.handleMouseEvents){
27543             btn.on("mouseover", this.onMouseOver, this);
27544             btn.on("mouseout", this.onMouseOut, this);
27545             btn.on("mousedown", this.onMouseDown, this);
27546         }
27547         btn.on(this.clickEvent, this.onClick, this);
27548         //btn.on("mouseup", this.onMouseUp, this);
27549         if(this.hidden){
27550             this.hide();
27551         }
27552         if(this.disabled){
27553             this.disable();
27554         }
27555         Roo.ButtonToggleMgr.register(this);
27556         if(this.pressed){
27557             this.el.addClass("x-btn-pressed");
27558         }
27559         if(this.repeat){
27560             var repeater = new Roo.util.ClickRepeater(btn,
27561                 typeof this.repeat == "object" ? this.repeat : {}
27562             );
27563             repeater.on("click", this.onClick,  this);
27564         }
27565         
27566         this.fireEvent('render', this);
27567         
27568     },
27569     /**
27570      * Returns the button's underlying element
27571      * @return {Roo.Element} The element
27572      */
27573     getEl : function(){
27574         return this.el;  
27575     },
27576     
27577     /**
27578      * Destroys this Button and removes any listeners.
27579      */
27580     destroy : function(){
27581         Roo.ButtonToggleMgr.unregister(this);
27582         this.el.removeAllListeners();
27583         this.purgeListeners();
27584         this.el.remove();
27585     },
27586
27587     // private
27588     autoWidth : function(){
27589         if(this.el){
27590             this.el.setWidth("auto");
27591             if(Roo.isIE7 && Roo.isStrict){
27592                 var ib = this.el.child('button');
27593                 if(ib && ib.getWidth() > 20){
27594                     ib.clip();
27595                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27596                 }
27597             }
27598             if(this.minWidth){
27599                 if(this.hidden){
27600                     this.el.beginMeasure();
27601                 }
27602                 if(this.el.getWidth() < this.minWidth){
27603                     this.el.setWidth(this.minWidth);
27604                 }
27605                 if(this.hidden){
27606                     this.el.endMeasure();
27607                 }
27608             }
27609         }
27610     },
27611
27612     /**
27613      * Assigns this button's click handler
27614      * @param {Function} handler The function to call when the button is clicked
27615      * @param {Object} scope (optional) Scope for the function passed in
27616      */
27617     setHandler : function(handler, scope){
27618         this.handler = handler;
27619         this.scope = scope;  
27620     },
27621     
27622     /**
27623      * Sets this button's text
27624      * @param {String} text The button text
27625      */
27626     setText : function(text){
27627         this.text = text;
27628         if(this.el){
27629             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27630         }
27631         this.autoWidth();
27632     },
27633     
27634     /**
27635      * Gets the text for this button
27636      * @return {String} The button text
27637      */
27638     getText : function(){
27639         return this.text;  
27640     },
27641     
27642     /**
27643      * Show this button
27644      */
27645     show: function(){
27646         this.hidden = false;
27647         if(this.el){
27648             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27649         }
27650     },
27651     
27652     /**
27653      * Hide this button
27654      */
27655     hide: function(){
27656         this.hidden = true;
27657         if(this.el){
27658             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27659         }
27660     },
27661     
27662     /**
27663      * Convenience function for boolean show/hide
27664      * @param {Boolean} visible True to show, false to hide
27665      */
27666     setVisible: function(visible){
27667         if(visible) {
27668             this.show();
27669         }else{
27670             this.hide();
27671         }
27672     },
27673     
27674     /**
27675      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27676      * @param {Boolean} state (optional) Force a particular state
27677      */
27678     toggle : function(state){
27679         state = state === undefined ? !this.pressed : state;
27680         if(state != this.pressed){
27681             if(state){
27682                 this.el.addClass("x-btn-pressed");
27683                 this.pressed = true;
27684                 this.fireEvent("toggle", this, true);
27685             }else{
27686                 this.el.removeClass("x-btn-pressed");
27687                 this.pressed = false;
27688                 this.fireEvent("toggle", this, false);
27689             }
27690             if(this.toggleHandler){
27691                 this.toggleHandler.call(this.scope || this, this, state);
27692             }
27693         }
27694     },
27695     
27696     /**
27697      * Focus the button
27698      */
27699     focus : function(){
27700         this.el.child('button:first').focus();
27701     },
27702     
27703     /**
27704      * Disable this button
27705      */
27706     disable : function(){
27707         if(this.el){
27708             this.el.addClass("x-btn-disabled");
27709         }
27710         this.disabled = true;
27711     },
27712     
27713     /**
27714      * Enable this button
27715      */
27716     enable : function(){
27717         if(this.el){
27718             this.el.removeClass("x-btn-disabled");
27719         }
27720         this.disabled = false;
27721     },
27722
27723     /**
27724      * Convenience function for boolean enable/disable
27725      * @param {Boolean} enabled True to enable, false to disable
27726      */
27727     setDisabled : function(v){
27728         this[v !== true ? "enable" : "disable"]();
27729     },
27730
27731     // private
27732     onClick : function(e){
27733         if(e){
27734             e.preventDefault();
27735         }
27736         if(e.button != 0){
27737             return;
27738         }
27739         if(!this.disabled){
27740             if(this.enableToggle){
27741                 this.toggle();
27742             }
27743             if(this.menu && !this.menu.isVisible()){
27744                 this.menu.show(this.el, this.menuAlign);
27745             }
27746             this.fireEvent("click", this, e);
27747             if(this.handler){
27748                 this.el.removeClass("x-btn-over");
27749                 this.handler.call(this.scope || this, this, e);
27750             }
27751         }
27752     },
27753     // private
27754     onMouseOver : function(e){
27755         if(!this.disabled){
27756             this.el.addClass("x-btn-over");
27757             this.fireEvent('mouseover', this, e);
27758         }
27759     },
27760     // private
27761     onMouseOut : function(e){
27762         if(!e.within(this.el,  true)){
27763             this.el.removeClass("x-btn-over");
27764             this.fireEvent('mouseout', this, e);
27765         }
27766     },
27767     // private
27768     onFocus : function(e){
27769         if(!this.disabled){
27770             this.el.addClass("x-btn-focus");
27771         }
27772     },
27773     // private
27774     onBlur : function(e){
27775         this.el.removeClass("x-btn-focus");
27776     },
27777     // private
27778     onMouseDown : function(e){
27779         if(!this.disabled && e.button == 0){
27780             this.el.addClass("x-btn-click");
27781             Roo.get(document).on('mouseup', this.onMouseUp, this);
27782         }
27783     },
27784     // private
27785     onMouseUp : function(e){
27786         if(e.button == 0){
27787             this.el.removeClass("x-btn-click");
27788             Roo.get(document).un('mouseup', this.onMouseUp, this);
27789         }
27790     },
27791     // private
27792     onMenuShow : function(e){
27793         this.el.addClass("x-btn-menu-active");
27794     },
27795     // private
27796     onMenuHide : function(e){
27797         this.el.removeClass("x-btn-menu-active");
27798     }   
27799 });
27800
27801 // Private utility class used by Button
27802 Roo.ButtonToggleMgr = function(){
27803    var groups = {};
27804    
27805    function toggleGroup(btn, state){
27806        if(state){
27807            var g = groups[btn.toggleGroup];
27808            for(var i = 0, l = g.length; i < l; i++){
27809                if(g[i] != btn){
27810                    g[i].toggle(false);
27811                }
27812            }
27813        }
27814    }
27815    
27816    return {
27817        register : function(btn){
27818            if(!btn.toggleGroup){
27819                return;
27820            }
27821            var g = groups[btn.toggleGroup];
27822            if(!g){
27823                g = groups[btn.toggleGroup] = [];
27824            }
27825            g.push(btn);
27826            btn.on("toggle", toggleGroup);
27827        },
27828        
27829        unregister : function(btn){
27830            if(!btn.toggleGroup){
27831                return;
27832            }
27833            var g = groups[btn.toggleGroup];
27834            if(g){
27835                g.remove(btn);
27836                btn.un("toggle", toggleGroup);
27837            }
27838        }
27839    };
27840 }();/*
27841  * Based on:
27842  * Ext JS Library 1.1.1
27843  * Copyright(c) 2006-2007, Ext JS, LLC.
27844  *
27845  * Originally Released Under LGPL - original licence link has changed is not relivant.
27846  *
27847  * Fork - LGPL
27848  * <script type="text/javascript">
27849  */
27850  
27851 /**
27852  * @class Roo.SplitButton
27853  * @extends Roo.Button
27854  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27855  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27856  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27857  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27858  * @cfg {String} arrowTooltip The title attribute of the arrow
27859  * @constructor
27860  * Create a new menu button
27861  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27862  * @param {Object} config The config object
27863  */
27864 Roo.SplitButton = function(renderTo, config){
27865     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27866     /**
27867      * @event arrowclick
27868      * Fires when this button's arrow is clicked
27869      * @param {SplitButton} this
27870      * @param {EventObject} e The click event
27871      */
27872     this.addEvents({"arrowclick":true});
27873 };
27874
27875 Roo.extend(Roo.SplitButton, Roo.Button, {
27876     render : function(renderTo){
27877         // this is one sweet looking template!
27878         var tpl = new Roo.Template(
27879             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27880             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27881             '<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>',
27882             "</tbody></table></td><td>",
27883             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27884             '<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>',
27885             "</tbody></table></td></tr></table>"
27886         );
27887         var btn = tpl.append(renderTo, [this.text, this.type], true);
27888         var btnEl = btn.child("button");
27889         if(this.cls){
27890             btn.addClass(this.cls);
27891         }
27892         if(this.icon){
27893             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27894         }
27895         if(this.iconCls){
27896             btnEl.addClass(this.iconCls);
27897             if(!this.cls){
27898                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27899             }
27900         }
27901         this.el = btn;
27902         if(this.handleMouseEvents){
27903             btn.on("mouseover", this.onMouseOver, this);
27904             btn.on("mouseout", this.onMouseOut, this);
27905             btn.on("mousedown", this.onMouseDown, this);
27906             btn.on("mouseup", this.onMouseUp, this);
27907         }
27908         btn.on(this.clickEvent, this.onClick, this);
27909         if(this.tooltip){
27910             if(typeof this.tooltip == 'object'){
27911                 Roo.QuickTips.tips(Roo.apply({
27912                       target: btnEl.id
27913                 }, this.tooltip));
27914             } else {
27915                 btnEl.dom[this.tooltipType] = this.tooltip;
27916             }
27917         }
27918         if(this.arrowTooltip){
27919             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27920         }
27921         if(this.hidden){
27922             this.hide();
27923         }
27924         if(this.disabled){
27925             this.disable();
27926         }
27927         if(this.pressed){
27928             this.el.addClass("x-btn-pressed");
27929         }
27930         if(Roo.isIE && !Roo.isIE7){
27931             this.autoWidth.defer(1, this);
27932         }else{
27933             this.autoWidth();
27934         }
27935         if(this.menu){
27936             this.menu.on("show", this.onMenuShow, this);
27937             this.menu.on("hide", this.onMenuHide, this);
27938         }
27939         this.fireEvent('render', this);
27940     },
27941
27942     // private
27943     autoWidth : function(){
27944         if(this.el){
27945             var tbl = this.el.child("table:first");
27946             var tbl2 = this.el.child("table:last");
27947             this.el.setWidth("auto");
27948             tbl.setWidth("auto");
27949             if(Roo.isIE7 && Roo.isStrict){
27950                 var ib = this.el.child('button:first');
27951                 if(ib && ib.getWidth() > 20){
27952                     ib.clip();
27953                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27954                 }
27955             }
27956             if(this.minWidth){
27957                 if(this.hidden){
27958                     this.el.beginMeasure();
27959                 }
27960                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27961                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27962                 }
27963                 if(this.hidden){
27964                     this.el.endMeasure();
27965                 }
27966             }
27967             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27968         } 
27969     },
27970     /**
27971      * Sets this button's click handler
27972      * @param {Function} handler The function to call when the button is clicked
27973      * @param {Object} scope (optional) Scope for the function passed above
27974      */
27975     setHandler : function(handler, scope){
27976         this.handler = handler;
27977         this.scope = scope;  
27978     },
27979     
27980     /**
27981      * Sets this button's arrow click handler
27982      * @param {Function} handler The function to call when the arrow is clicked
27983      * @param {Object} scope (optional) Scope for the function passed above
27984      */
27985     setArrowHandler : function(handler, scope){
27986         this.arrowHandler = handler;
27987         this.scope = scope;  
27988     },
27989     
27990     /**
27991      * Focus the button
27992      */
27993     focus : function(){
27994         if(this.el){
27995             this.el.child("button:first").focus();
27996         }
27997     },
27998
27999     // private
28000     onClick : function(e){
28001         e.preventDefault();
28002         if(!this.disabled){
28003             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28004                 if(this.menu && !this.menu.isVisible()){
28005                     this.menu.show(this.el, this.menuAlign);
28006                 }
28007                 this.fireEvent("arrowclick", this, e);
28008                 if(this.arrowHandler){
28009                     this.arrowHandler.call(this.scope || this, this, e);
28010                 }
28011             }else{
28012                 this.fireEvent("click", this, e);
28013                 if(this.handler){
28014                     this.handler.call(this.scope || this, this, e);
28015                 }
28016             }
28017         }
28018     },
28019     // private
28020     onMouseDown : function(e){
28021         if(!this.disabled){
28022             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28023         }
28024     },
28025     // private
28026     onMouseUp : function(e){
28027         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28028     }   
28029 });
28030
28031
28032 // backwards compat
28033 Roo.MenuButton = Roo.SplitButton;/*
28034  * Based on:
28035  * Ext JS Library 1.1.1
28036  * Copyright(c) 2006-2007, Ext JS, LLC.
28037  *
28038  * Originally Released Under LGPL - original licence link has changed is not relivant.
28039  *
28040  * Fork - LGPL
28041  * <script type="text/javascript">
28042  */
28043
28044 /**
28045  * @class Roo.Toolbar
28046  * Basic Toolbar class.
28047  * @constructor
28048  * Creates a new Toolbar
28049  * @param {Object} container The config object
28050  */ 
28051 Roo.Toolbar = function(container, buttons, config)
28052 {
28053     /// old consturctor format still supported..
28054     if(container instanceof Array){ // omit the container for later rendering
28055         buttons = container;
28056         config = buttons;
28057         container = null;
28058     }
28059     if (typeof(container) == 'object' && container.xtype) {
28060         config = container;
28061         container = config.container;
28062         buttons = config.buttons || []; // not really - use items!!
28063     }
28064     var xitems = [];
28065     if (config && config.items) {
28066         xitems = config.items;
28067         delete config.items;
28068     }
28069     Roo.apply(this, config);
28070     this.buttons = buttons;
28071     
28072     if(container){
28073         this.render(container);
28074     }
28075     this.xitems = xitems;
28076     Roo.each(xitems, function(b) {
28077         this.add(b);
28078     }, this);
28079     
28080 };
28081
28082 Roo.Toolbar.prototype = {
28083     /**
28084      * @cfg {Array} items
28085      * array of button configs or elements to add (will be converted to a MixedCollection)
28086      */
28087     
28088     /**
28089      * @cfg {String/HTMLElement/Element} container
28090      * The id or element that will contain the toolbar
28091      */
28092     // private
28093     render : function(ct){
28094         this.el = Roo.get(ct);
28095         if(this.cls){
28096             this.el.addClass(this.cls);
28097         }
28098         // using a table allows for vertical alignment
28099         // 100% width is needed by Safari...
28100         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28101         this.tr = this.el.child("tr", true);
28102         var autoId = 0;
28103         this.items = new Roo.util.MixedCollection(false, function(o){
28104             return o.id || ("item" + (++autoId));
28105         });
28106         if(this.buttons){
28107             this.add.apply(this, this.buttons);
28108             delete this.buttons;
28109         }
28110     },
28111
28112     /**
28113      * Adds element(s) to the toolbar -- this function takes a variable number of 
28114      * arguments of mixed type and adds them to the toolbar.
28115      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28116      * <ul>
28117      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28118      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28119      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28120      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28121      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28122      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28123      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28124      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28125      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28126      * </ul>
28127      * @param {Mixed} arg2
28128      * @param {Mixed} etc.
28129      */
28130     add : function(){
28131         var a = arguments, l = a.length;
28132         for(var i = 0; i < l; i++){
28133             this._add(a[i]);
28134         }
28135     },
28136     // private..
28137     _add : function(el) {
28138         
28139         if (el.xtype) {
28140             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28141         }
28142         
28143         if (el.applyTo){ // some kind of form field
28144             return this.addField(el);
28145         } 
28146         if (el.render){ // some kind of Toolbar.Item
28147             return this.addItem(el);
28148         }
28149         if (typeof el == "string"){ // string
28150             if(el == "separator" || el == "-"){
28151                 return this.addSeparator();
28152             }
28153             if (el == " "){
28154                 return this.addSpacer();
28155             }
28156             if(el == "->"){
28157                 return this.addFill();
28158             }
28159             return this.addText(el);
28160             
28161         }
28162         if(el.tagName){ // element
28163             return this.addElement(el);
28164         }
28165         if(typeof el == "object"){ // must be button config?
28166             return this.addButton(el);
28167         }
28168         // and now what?!?!
28169         return false;
28170         
28171     },
28172     
28173     /**
28174      * Add an Xtype element
28175      * @param {Object} xtype Xtype Object
28176      * @return {Object} created Object
28177      */
28178     addxtype : function(e){
28179         return this.add(e);  
28180     },
28181     
28182     /**
28183      * Returns the Element for this toolbar.
28184      * @return {Roo.Element}
28185      */
28186     getEl : function(){
28187         return this.el;  
28188     },
28189     
28190     /**
28191      * Adds a separator
28192      * @return {Roo.Toolbar.Item} The separator item
28193      */
28194     addSeparator : function(){
28195         return this.addItem(new Roo.Toolbar.Separator());
28196     },
28197
28198     /**
28199      * Adds a spacer element
28200      * @return {Roo.Toolbar.Spacer} The spacer item
28201      */
28202     addSpacer : function(){
28203         return this.addItem(new Roo.Toolbar.Spacer());
28204     },
28205
28206     /**
28207      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28208      * @return {Roo.Toolbar.Fill} The fill item
28209      */
28210     addFill : function(){
28211         return this.addItem(new Roo.Toolbar.Fill());
28212     },
28213
28214     /**
28215      * Adds any standard HTML element to the toolbar
28216      * @param {String/HTMLElement/Element} el The element or id of the element to add
28217      * @return {Roo.Toolbar.Item} The element's item
28218      */
28219     addElement : function(el){
28220         return this.addItem(new Roo.Toolbar.Item(el));
28221     },
28222     /**
28223      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28224      * @type Roo.util.MixedCollection  
28225      */
28226     items : false,
28227      
28228     /**
28229      * Adds any Toolbar.Item or subclass
28230      * @param {Roo.Toolbar.Item} item
28231      * @return {Roo.Toolbar.Item} The item
28232      */
28233     addItem : function(item){
28234         var td = this.nextBlock();
28235         item.render(td);
28236         this.items.add(item);
28237         return item;
28238     },
28239     
28240     /**
28241      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28242      * @param {Object/Array} config A button config or array of configs
28243      * @return {Roo.Toolbar.Button/Array}
28244      */
28245     addButton : function(config){
28246         if(config instanceof Array){
28247             var buttons = [];
28248             for(var i = 0, len = config.length; i < len; i++) {
28249                 buttons.push(this.addButton(config[i]));
28250             }
28251             return buttons;
28252         }
28253         var b = config;
28254         if(!(config instanceof Roo.Toolbar.Button)){
28255             b = config.split ?
28256                 new Roo.Toolbar.SplitButton(config) :
28257                 new Roo.Toolbar.Button(config);
28258         }
28259         var td = this.nextBlock();
28260         b.render(td);
28261         this.items.add(b);
28262         return b;
28263     },
28264     
28265     /**
28266      * Adds text to the toolbar
28267      * @param {String} text The text to add
28268      * @return {Roo.Toolbar.Item} The element's item
28269      */
28270     addText : function(text){
28271         return this.addItem(new Roo.Toolbar.TextItem(text));
28272     },
28273     
28274     /**
28275      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28276      * @param {Number} index The index where the item is to be inserted
28277      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28278      * @return {Roo.Toolbar.Button/Item}
28279      */
28280     insertButton : function(index, item){
28281         if(item instanceof Array){
28282             var buttons = [];
28283             for(var i = 0, len = item.length; i < len; i++) {
28284                buttons.push(this.insertButton(index + i, item[i]));
28285             }
28286             return buttons;
28287         }
28288         if (!(item instanceof Roo.Toolbar.Button)){
28289            item = new Roo.Toolbar.Button(item);
28290         }
28291         var td = document.createElement("td");
28292         this.tr.insertBefore(td, this.tr.childNodes[index]);
28293         item.render(td);
28294         this.items.insert(index, item);
28295         return item;
28296     },
28297     
28298     /**
28299      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28300      * @param {Object} config
28301      * @return {Roo.Toolbar.Item} The element's item
28302      */
28303     addDom : function(config, returnEl){
28304         var td = this.nextBlock();
28305         Roo.DomHelper.overwrite(td, config);
28306         var ti = new Roo.Toolbar.Item(td.firstChild);
28307         ti.render(td);
28308         this.items.add(ti);
28309         return ti;
28310     },
28311
28312     /**
28313      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28314      * @type Roo.util.MixedCollection  
28315      */
28316     fields : false,
28317     
28318     /**
28319      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28320      * Note: the field should not have been rendered yet. For a field that has already been
28321      * rendered, use {@link #addElement}.
28322      * @param {Roo.form.Field} field
28323      * @return {Roo.ToolbarItem}
28324      */
28325      
28326       
28327     addField : function(field) {
28328         if (!this.fields) {
28329             var autoId = 0;
28330             this.fields = new Roo.util.MixedCollection(false, function(o){
28331                 return o.id || ("item" + (++autoId));
28332             });
28333
28334         }
28335         
28336         var td = this.nextBlock();
28337         field.render(td);
28338         var ti = new Roo.Toolbar.Item(td.firstChild);
28339         ti.render(td);
28340         this.items.add(ti);
28341         this.fields.add(field);
28342         return ti;
28343     },
28344     /**
28345      * Hide the toolbar
28346      * @method hide
28347      */
28348      
28349       
28350     hide : function()
28351     {
28352         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28353         this.el.child('div').hide();
28354     },
28355     /**
28356      * Show the toolbar
28357      * @method show
28358      */
28359     show : function()
28360     {
28361         this.el.child('div').show();
28362     },
28363       
28364     // private
28365     nextBlock : function(){
28366         var td = document.createElement("td");
28367         this.tr.appendChild(td);
28368         return td;
28369     },
28370
28371     // private
28372     destroy : function(){
28373         if(this.items){ // rendered?
28374             Roo.destroy.apply(Roo, this.items.items);
28375         }
28376         if(this.fields){ // rendered?
28377             Roo.destroy.apply(Roo, this.fields.items);
28378         }
28379         Roo.Element.uncache(this.el, this.tr);
28380     }
28381 };
28382
28383 /**
28384  * @class Roo.Toolbar.Item
28385  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28386  * @constructor
28387  * Creates a new Item
28388  * @param {HTMLElement} el 
28389  */
28390 Roo.Toolbar.Item = function(el){
28391     this.el = Roo.getDom(el);
28392     this.id = Roo.id(this.el);
28393     this.hidden = false;
28394 };
28395
28396 Roo.Toolbar.Item.prototype = {
28397     
28398     /**
28399      * Get this item's HTML Element
28400      * @return {HTMLElement}
28401      */
28402     getEl : function(){
28403        return this.el;  
28404     },
28405
28406     // private
28407     render : function(td){
28408         this.td = td;
28409         td.appendChild(this.el);
28410     },
28411     
28412     /**
28413      * Removes and destroys this item.
28414      */
28415     destroy : function(){
28416         this.td.parentNode.removeChild(this.td);
28417     },
28418     
28419     /**
28420      * Shows this item.
28421      */
28422     show: function(){
28423         this.hidden = false;
28424         this.td.style.display = "";
28425     },
28426     
28427     /**
28428      * Hides this item.
28429      */
28430     hide: function(){
28431         this.hidden = true;
28432         this.td.style.display = "none";
28433     },
28434     
28435     /**
28436      * Convenience function for boolean show/hide.
28437      * @param {Boolean} visible true to show/false to hide
28438      */
28439     setVisible: function(visible){
28440         if(visible) {
28441             this.show();
28442         }else{
28443             this.hide();
28444         }
28445     },
28446     
28447     /**
28448      * Try to focus this item.
28449      */
28450     focus : function(){
28451         Roo.fly(this.el).focus();
28452     },
28453     
28454     /**
28455      * Disables this item.
28456      */
28457     disable : function(){
28458         Roo.fly(this.td).addClass("x-item-disabled");
28459         this.disabled = true;
28460         this.el.disabled = true;
28461     },
28462     
28463     /**
28464      * Enables this item.
28465      */
28466     enable : function(){
28467         Roo.fly(this.td).removeClass("x-item-disabled");
28468         this.disabled = false;
28469         this.el.disabled = false;
28470     }
28471 };
28472
28473
28474 /**
28475  * @class Roo.Toolbar.Separator
28476  * @extends Roo.Toolbar.Item
28477  * A simple toolbar separator class
28478  * @constructor
28479  * Creates a new Separator
28480  */
28481 Roo.Toolbar.Separator = function(){
28482     var s = document.createElement("span");
28483     s.className = "ytb-sep";
28484     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28485 };
28486 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28487     enable:Roo.emptyFn,
28488     disable:Roo.emptyFn,
28489     focus:Roo.emptyFn
28490 });
28491
28492 /**
28493  * @class Roo.Toolbar.Spacer
28494  * @extends Roo.Toolbar.Item
28495  * A simple element that adds extra horizontal space to a toolbar.
28496  * @constructor
28497  * Creates a new Spacer
28498  */
28499 Roo.Toolbar.Spacer = function(){
28500     var s = document.createElement("div");
28501     s.className = "ytb-spacer";
28502     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28503 };
28504 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28505     enable:Roo.emptyFn,
28506     disable:Roo.emptyFn,
28507     focus:Roo.emptyFn
28508 });
28509
28510 /**
28511  * @class Roo.Toolbar.Fill
28512  * @extends Roo.Toolbar.Spacer
28513  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28514  * @constructor
28515  * Creates a new Spacer
28516  */
28517 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28518     // private
28519     render : function(td){
28520         td.style.width = '100%';
28521         Roo.Toolbar.Fill.superclass.render.call(this, td);
28522     }
28523 });
28524
28525 /**
28526  * @class Roo.Toolbar.TextItem
28527  * @extends Roo.Toolbar.Item
28528  * A simple class that renders text directly into a toolbar.
28529  * @constructor
28530  * Creates a new TextItem
28531  * @param {String} text
28532  */
28533 Roo.Toolbar.TextItem = function(text){
28534     if (typeof(text) == 'object') {
28535         text = text.text;
28536     }
28537     var s = document.createElement("span");
28538     s.className = "ytb-text";
28539     s.innerHTML = text;
28540     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28541 };
28542 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28543     enable:Roo.emptyFn,
28544     disable:Roo.emptyFn,
28545     focus:Roo.emptyFn
28546 });
28547
28548 /**
28549  * @class Roo.Toolbar.Button
28550  * @extends Roo.Button
28551  * A button that renders into a toolbar.
28552  * @constructor
28553  * Creates a new Button
28554  * @param {Object} config A standard {@link Roo.Button} config object
28555  */
28556 Roo.Toolbar.Button = function(config){
28557     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28558 };
28559 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28560     render : function(td){
28561         this.td = td;
28562         Roo.Toolbar.Button.superclass.render.call(this, td);
28563     },
28564     
28565     /**
28566      * Removes and destroys this button
28567      */
28568     destroy : function(){
28569         Roo.Toolbar.Button.superclass.destroy.call(this);
28570         this.td.parentNode.removeChild(this.td);
28571     },
28572     
28573     /**
28574      * Shows this button
28575      */
28576     show: function(){
28577         this.hidden = false;
28578         this.td.style.display = "";
28579     },
28580     
28581     /**
28582      * Hides this button
28583      */
28584     hide: function(){
28585         this.hidden = true;
28586         this.td.style.display = "none";
28587     },
28588
28589     /**
28590      * Disables this item
28591      */
28592     disable : function(){
28593         Roo.fly(this.td).addClass("x-item-disabled");
28594         this.disabled = true;
28595     },
28596
28597     /**
28598      * Enables this item
28599      */
28600     enable : function(){
28601         Roo.fly(this.td).removeClass("x-item-disabled");
28602         this.disabled = false;
28603     }
28604 });
28605 // backwards compat
28606 Roo.ToolbarButton = Roo.Toolbar.Button;
28607
28608 /**
28609  * @class Roo.Toolbar.SplitButton
28610  * @extends Roo.SplitButton
28611  * A menu button that renders into a toolbar.
28612  * @constructor
28613  * Creates a new SplitButton
28614  * @param {Object} config A standard {@link Roo.SplitButton} config object
28615  */
28616 Roo.Toolbar.SplitButton = function(config){
28617     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28618 };
28619 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28620     render : function(td){
28621         this.td = td;
28622         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28623     },
28624     
28625     /**
28626      * Removes and destroys this button
28627      */
28628     destroy : function(){
28629         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28630         this.td.parentNode.removeChild(this.td);
28631     },
28632     
28633     /**
28634      * Shows this button
28635      */
28636     show: function(){
28637         this.hidden = false;
28638         this.td.style.display = "";
28639     },
28640     
28641     /**
28642      * Hides this button
28643      */
28644     hide: function(){
28645         this.hidden = true;
28646         this.td.style.display = "none";
28647     }
28648 });
28649
28650 // backwards compat
28651 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28652  * Based on:
28653  * Ext JS Library 1.1.1
28654  * Copyright(c) 2006-2007, Ext JS, LLC.
28655  *
28656  * Originally Released Under LGPL - original licence link has changed is not relivant.
28657  *
28658  * Fork - LGPL
28659  * <script type="text/javascript">
28660  */
28661  
28662 /**
28663  * @class Roo.PagingToolbar
28664  * @extends Roo.Toolbar
28665  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28666  * @constructor
28667  * Create a new PagingToolbar
28668  * @param {Object} config The config object
28669  */
28670 Roo.PagingToolbar = function(el, ds, config)
28671 {
28672     // old args format still supported... - xtype is prefered..
28673     if (typeof(el) == 'object' && el.xtype) {
28674         // created from xtype...
28675         config = el;
28676         ds = el.dataSource;
28677         el = config.container;
28678     }
28679     var items = [];
28680     if (config.items) {
28681         items = config.items;
28682         config.items = [];
28683     }
28684     
28685     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28686     this.ds = ds;
28687     this.cursor = 0;
28688     this.renderButtons(this.el);
28689     this.bind(ds);
28690     
28691     // supprot items array.
28692    
28693     Roo.each(items, function(e) {
28694         this.add(Roo.factory(e));
28695     },this);
28696     
28697 };
28698
28699 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28700     /**
28701      * @cfg {Roo.data.Store} dataSource
28702      * The underlying data store providing the paged data
28703      */
28704     /**
28705      * @cfg {String/HTMLElement/Element} container
28706      * container The id or element that will contain the toolbar
28707      */
28708     /**
28709      * @cfg {Boolean} displayInfo
28710      * True to display the displayMsg (defaults to false)
28711      */
28712     /**
28713      * @cfg {Number} pageSize
28714      * The number of records to display per page (defaults to 20)
28715      */
28716     pageSize: 20,
28717     /**
28718      * @cfg {String} displayMsg
28719      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28720      */
28721     displayMsg : 'Displaying {0} - {1} of {2}',
28722     /**
28723      * @cfg {String} emptyMsg
28724      * The message to display when no records are found (defaults to "No data to display")
28725      */
28726     emptyMsg : 'No data to display',
28727     /**
28728      * Customizable piece of the default paging text (defaults to "Page")
28729      * @type String
28730      */
28731     beforePageText : "Page",
28732     /**
28733      * Customizable piece of the default paging text (defaults to "of %0")
28734      * @type String
28735      */
28736     afterPageText : "of {0}",
28737     /**
28738      * Customizable piece of the default paging text (defaults to "First Page")
28739      * @type String
28740      */
28741     firstText : "First Page",
28742     /**
28743      * Customizable piece of the default paging text (defaults to "Previous Page")
28744      * @type String
28745      */
28746     prevText : "Previous Page",
28747     /**
28748      * Customizable piece of the default paging text (defaults to "Next Page")
28749      * @type String
28750      */
28751     nextText : "Next Page",
28752     /**
28753      * Customizable piece of the default paging text (defaults to "Last Page")
28754      * @type String
28755      */
28756     lastText : "Last Page",
28757     /**
28758      * Customizable piece of the default paging text (defaults to "Refresh")
28759      * @type String
28760      */
28761     refreshText : "Refresh",
28762
28763     // private
28764     renderButtons : function(el){
28765         Roo.PagingToolbar.superclass.render.call(this, el);
28766         this.first = this.addButton({
28767             tooltip: this.firstText,
28768             cls: "x-btn-icon x-grid-page-first",
28769             disabled: true,
28770             handler: this.onClick.createDelegate(this, ["first"])
28771         });
28772         this.prev = this.addButton({
28773             tooltip: this.prevText,
28774             cls: "x-btn-icon x-grid-page-prev",
28775             disabled: true,
28776             handler: this.onClick.createDelegate(this, ["prev"])
28777         });
28778         //this.addSeparator();
28779         this.add(this.beforePageText);
28780         this.field = Roo.get(this.addDom({
28781            tag: "input",
28782            type: "text",
28783            size: "3",
28784            value: "1",
28785            cls: "x-grid-page-number"
28786         }).el);
28787         this.field.on("keydown", this.onPagingKeydown, this);
28788         this.field.on("focus", function(){this.dom.select();});
28789         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28790         this.field.setHeight(18);
28791         //this.addSeparator();
28792         this.next = this.addButton({
28793             tooltip: this.nextText,
28794             cls: "x-btn-icon x-grid-page-next",
28795             disabled: true,
28796             handler: this.onClick.createDelegate(this, ["next"])
28797         });
28798         this.last = this.addButton({
28799             tooltip: this.lastText,
28800             cls: "x-btn-icon x-grid-page-last",
28801             disabled: true,
28802             handler: this.onClick.createDelegate(this, ["last"])
28803         });
28804         //this.addSeparator();
28805         this.loading = this.addButton({
28806             tooltip: this.refreshText,
28807             cls: "x-btn-icon x-grid-loading",
28808             handler: this.onClick.createDelegate(this, ["refresh"])
28809         });
28810
28811         if(this.displayInfo){
28812             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28813         }
28814     },
28815
28816     // private
28817     updateInfo : function(){
28818         if(this.displayEl){
28819             var count = this.ds.getCount();
28820             var msg = count == 0 ?
28821                 this.emptyMsg :
28822                 String.format(
28823                     this.displayMsg,
28824                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28825                 );
28826             this.displayEl.update(msg);
28827         }
28828     },
28829
28830     // private
28831     onLoad : function(ds, r, o){
28832        this.cursor = o.params ? o.params.start : 0;
28833        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28834
28835        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28836        this.field.dom.value = ap;
28837        this.first.setDisabled(ap == 1);
28838        this.prev.setDisabled(ap == 1);
28839        this.next.setDisabled(ap == ps);
28840        this.last.setDisabled(ap == ps);
28841        this.loading.enable();
28842        this.updateInfo();
28843     },
28844
28845     // private
28846     getPageData : function(){
28847         var total = this.ds.getTotalCount();
28848         return {
28849             total : total,
28850             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28851             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28852         };
28853     },
28854
28855     // private
28856     onLoadError : function(){
28857         this.loading.enable();
28858     },
28859
28860     // private
28861     onPagingKeydown : function(e){
28862         var k = e.getKey();
28863         var d = this.getPageData();
28864         if(k == e.RETURN){
28865             var v = this.field.dom.value, pageNum;
28866             if(!v || isNaN(pageNum = parseInt(v, 10))){
28867                 this.field.dom.value = d.activePage;
28868                 return;
28869             }
28870             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28871             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28872             e.stopEvent();
28873         }
28874         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))
28875         {
28876           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28877           this.field.dom.value = pageNum;
28878           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28879           e.stopEvent();
28880         }
28881         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28882         {
28883           var v = this.field.dom.value, pageNum; 
28884           var increment = (e.shiftKey) ? 10 : 1;
28885           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28886             increment *= -1;
28887           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28888             this.field.dom.value = d.activePage;
28889             return;
28890           }
28891           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28892           {
28893             this.field.dom.value = parseInt(v, 10) + increment;
28894             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28895             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28896           }
28897           e.stopEvent();
28898         }
28899     },
28900
28901     // private
28902     beforeLoad : function(){
28903         if(this.loading){
28904             this.loading.disable();
28905         }
28906     },
28907
28908     // private
28909     onClick : function(which){
28910         var ds = this.ds;
28911         switch(which){
28912             case "first":
28913                 ds.load({params:{start: 0, limit: this.pageSize}});
28914             break;
28915             case "prev":
28916                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28917             break;
28918             case "next":
28919                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28920             break;
28921             case "last":
28922                 var total = ds.getTotalCount();
28923                 var extra = total % this.pageSize;
28924                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28925                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28926             break;
28927             case "refresh":
28928                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28929             break;
28930         }
28931     },
28932
28933     /**
28934      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28935      * @param {Roo.data.Store} store The data store to unbind
28936      */
28937     unbind : function(ds){
28938         ds.un("beforeload", this.beforeLoad, this);
28939         ds.un("load", this.onLoad, this);
28940         ds.un("loadexception", this.onLoadError, this);
28941         ds.un("remove", this.updateInfo, this);
28942         ds.un("add", this.updateInfo, this);
28943         this.ds = undefined;
28944     },
28945
28946     /**
28947      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28948      * @param {Roo.data.Store} store The data store to bind
28949      */
28950     bind : function(ds){
28951         ds.on("beforeload", this.beforeLoad, this);
28952         ds.on("load", this.onLoad, this);
28953         ds.on("loadexception", this.onLoadError, this);
28954         ds.on("remove", this.updateInfo, this);
28955         ds.on("add", this.updateInfo, this);
28956         this.ds = ds;
28957     }
28958 });/*
28959  * Based on:
28960  * Ext JS Library 1.1.1
28961  * Copyright(c) 2006-2007, Ext JS, LLC.
28962  *
28963  * Originally Released Under LGPL - original licence link has changed is not relivant.
28964  *
28965  * Fork - LGPL
28966  * <script type="text/javascript">
28967  */
28968
28969 /**
28970  * @class Roo.Resizable
28971  * @extends Roo.util.Observable
28972  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28973  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28974  * 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
28975  * the element will be wrapped for you automatically.</p>
28976  * <p>Here is the list of valid resize handles:</p>
28977  * <pre>
28978 Value   Description
28979 ------  -------------------
28980  'n'     north
28981  's'     south
28982  'e'     east
28983  'w'     west
28984  'nw'    northwest
28985  'sw'    southwest
28986  'se'    southeast
28987  'ne'    northeast
28988  'hd'    horizontal drag
28989  'all'   all
28990 </pre>
28991  * <p>Here's an example showing the creation of a typical Resizable:</p>
28992  * <pre><code>
28993 var resizer = new Roo.Resizable("element-id", {
28994     handles: 'all',
28995     minWidth: 200,
28996     minHeight: 100,
28997     maxWidth: 500,
28998     maxHeight: 400,
28999     pinned: true
29000 });
29001 resizer.on("resize", myHandler);
29002 </code></pre>
29003  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29004  * resizer.east.setDisplayed(false);</p>
29005  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29006  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29007  * resize operation's new size (defaults to [0, 0])
29008  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29009  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29010  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29011  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29012  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29013  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29014  * @cfg {Number} width The width of the element in pixels (defaults to null)
29015  * @cfg {Number} height The height of the element in pixels (defaults to null)
29016  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29017  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29018  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29019  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29020  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29021  * in favor of the handles config option (defaults to false)
29022  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29023  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29024  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29025  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29026  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29027  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29028  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29029  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29030  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29031  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29032  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29033  * @constructor
29034  * Create a new resizable component
29035  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29036  * @param {Object} config configuration options
29037   */
29038 Roo.Resizable = function(el, config)
29039 {
29040     this.el = Roo.get(el);
29041
29042     if(config && config.wrap){
29043         config.resizeChild = this.el;
29044         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29045         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29046         this.el.setStyle("overflow", "hidden");
29047         this.el.setPositioning(config.resizeChild.getPositioning());
29048         config.resizeChild.clearPositioning();
29049         if(!config.width || !config.height){
29050             var csize = config.resizeChild.getSize();
29051             this.el.setSize(csize.width, csize.height);
29052         }
29053         if(config.pinned && !config.adjustments){
29054             config.adjustments = "auto";
29055         }
29056     }
29057
29058     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29059     this.proxy.unselectable();
29060     this.proxy.enableDisplayMode('block');
29061
29062     Roo.apply(this, config);
29063
29064     if(this.pinned){
29065         this.disableTrackOver = true;
29066         this.el.addClass("x-resizable-pinned");
29067     }
29068     // if the element isn't positioned, make it relative
29069     var position = this.el.getStyle("position");
29070     if(position != "absolute" && position != "fixed"){
29071         this.el.setStyle("position", "relative");
29072     }
29073     if(!this.handles){ // no handles passed, must be legacy style
29074         this.handles = 's,e,se';
29075         if(this.multiDirectional){
29076             this.handles += ',n,w';
29077         }
29078     }
29079     if(this.handles == "all"){
29080         this.handles = "n s e w ne nw se sw";
29081     }
29082     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29083     var ps = Roo.Resizable.positions;
29084     for(var i = 0, len = hs.length; i < len; i++){
29085         if(hs[i] && ps[hs[i]]){
29086             var pos = ps[hs[i]];
29087             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29088         }
29089     }
29090     // legacy
29091     this.corner = this.southeast;
29092     
29093     // updateBox = the box can move..
29094     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29095         this.updateBox = true;
29096     }
29097
29098     this.activeHandle = null;
29099
29100     if(this.resizeChild){
29101         if(typeof this.resizeChild == "boolean"){
29102             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29103         }else{
29104             this.resizeChild = Roo.get(this.resizeChild, true);
29105         }
29106     }
29107     
29108     if(this.adjustments == "auto"){
29109         var rc = this.resizeChild;
29110         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29111         if(rc && (hw || hn)){
29112             rc.position("relative");
29113             rc.setLeft(hw ? hw.el.getWidth() : 0);
29114             rc.setTop(hn ? hn.el.getHeight() : 0);
29115         }
29116         this.adjustments = [
29117             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29118             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29119         ];
29120     }
29121
29122     if(this.draggable){
29123         this.dd = this.dynamic ?
29124             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29125         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29126     }
29127
29128     // public events
29129     this.addEvents({
29130         /**
29131          * @event beforeresize
29132          * Fired before resize is allowed. Set enabled to false to cancel resize.
29133          * @param {Roo.Resizable} this
29134          * @param {Roo.EventObject} e The mousedown event
29135          */
29136         "beforeresize" : true,
29137         /**
29138          * @event resizing
29139          * Fired a resizing.
29140          * @param {Roo.Resizable} this
29141          * @param {Number} x The new x position
29142          * @param {Number} y The new y position
29143          * @param {Number} w The new w width
29144          * @param {Number} h The new h hight
29145          * @param {Roo.EventObject} e The mouseup event
29146          */
29147         "resizing" : true,
29148         /**
29149          * @event resize
29150          * Fired after a resize.
29151          * @param {Roo.Resizable} this
29152          * @param {Number} width The new width
29153          * @param {Number} height The new height
29154          * @param {Roo.EventObject} e The mouseup event
29155          */
29156         "resize" : true
29157     });
29158
29159     if(this.width !== null && this.height !== null){
29160         this.resizeTo(this.width, this.height);
29161     }else{
29162         this.updateChildSize();
29163     }
29164     if(Roo.isIE){
29165         this.el.dom.style.zoom = 1;
29166     }
29167     Roo.Resizable.superclass.constructor.call(this);
29168 };
29169
29170 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29171         resizeChild : false,
29172         adjustments : [0, 0],
29173         minWidth : 5,
29174         minHeight : 5,
29175         maxWidth : 10000,
29176         maxHeight : 10000,
29177         enabled : true,
29178         animate : false,
29179         duration : .35,
29180         dynamic : false,
29181         handles : false,
29182         multiDirectional : false,
29183         disableTrackOver : false,
29184         easing : 'easeOutStrong',
29185         widthIncrement : 0,
29186         heightIncrement : 0,
29187         pinned : false,
29188         width : null,
29189         height : null,
29190         preserveRatio : false,
29191         transparent: false,
29192         minX: 0,
29193         minY: 0,
29194         draggable: false,
29195
29196         /**
29197          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29198          */
29199         constrainTo: undefined,
29200         /**
29201          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29202          */
29203         resizeRegion: undefined,
29204
29205
29206     /**
29207      * Perform a manual resize
29208      * @param {Number} width
29209      * @param {Number} height
29210      */
29211     resizeTo : function(width, height){
29212         this.el.setSize(width, height);
29213         this.updateChildSize();
29214         this.fireEvent("resize", this, width, height, null);
29215     },
29216
29217     // private
29218     startSizing : function(e, handle){
29219         this.fireEvent("beforeresize", this, e);
29220         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29221
29222             if(!this.overlay){
29223                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29224                 this.overlay.unselectable();
29225                 this.overlay.enableDisplayMode("block");
29226                 this.overlay.on("mousemove", this.onMouseMove, this);
29227                 this.overlay.on("mouseup", this.onMouseUp, this);
29228             }
29229             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29230
29231             this.resizing = true;
29232             this.startBox = this.el.getBox();
29233             this.startPoint = e.getXY();
29234             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29235                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29236
29237             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29238             this.overlay.show();
29239
29240             if(this.constrainTo) {
29241                 var ct = Roo.get(this.constrainTo);
29242                 this.resizeRegion = ct.getRegion().adjust(
29243                     ct.getFrameWidth('t'),
29244                     ct.getFrameWidth('l'),
29245                     -ct.getFrameWidth('b'),
29246                     -ct.getFrameWidth('r')
29247                 );
29248             }
29249
29250             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29251             this.proxy.show();
29252             this.proxy.setBox(this.startBox);
29253             if(!this.dynamic){
29254                 this.proxy.setStyle('visibility', 'visible');
29255             }
29256         }
29257     },
29258
29259     // private
29260     onMouseDown : function(handle, e){
29261         if(this.enabled){
29262             e.stopEvent();
29263             this.activeHandle = handle;
29264             this.startSizing(e, handle);
29265         }
29266     },
29267
29268     // private
29269     onMouseUp : function(e){
29270         var size = this.resizeElement();
29271         this.resizing = false;
29272         this.handleOut();
29273         this.overlay.hide();
29274         this.proxy.hide();
29275         this.fireEvent("resize", this, size.width, size.height, e);
29276     },
29277
29278     // private
29279     updateChildSize : function(){
29280         
29281         if(this.resizeChild){
29282             var el = this.el;
29283             var child = this.resizeChild;
29284             var adj = this.adjustments;
29285             if(el.dom.offsetWidth){
29286                 var b = el.getSize(true);
29287                 child.setSize(b.width+adj[0], b.height+adj[1]);
29288             }
29289             // Second call here for IE
29290             // The first call enables instant resizing and
29291             // the second call corrects scroll bars if they
29292             // exist
29293             if(Roo.isIE){
29294                 setTimeout(function(){
29295                     if(el.dom.offsetWidth){
29296                         var b = el.getSize(true);
29297                         child.setSize(b.width+adj[0], b.height+adj[1]);
29298                     }
29299                 }, 10);
29300             }
29301         }
29302     },
29303
29304     // private
29305     snap : function(value, inc, min){
29306         if(!inc || !value) return value;
29307         var newValue = value;
29308         var m = value % inc;
29309         if(m > 0){
29310             if(m > (inc/2)){
29311                 newValue = value + (inc-m);
29312             }else{
29313                 newValue = value - m;
29314             }
29315         }
29316         return Math.max(min, newValue);
29317     },
29318
29319     // private
29320     resizeElement : function(){
29321         var box = this.proxy.getBox();
29322         if(this.updateBox){
29323             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29324         }else{
29325             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29326         }
29327         this.updateChildSize();
29328         if(!this.dynamic){
29329             this.proxy.hide();
29330         }
29331         return box;
29332     },
29333
29334     // private
29335     constrain : function(v, diff, m, mx){
29336         if(v - diff < m){
29337             diff = v - m;
29338         }else if(v - diff > mx){
29339             diff = mx - v;
29340         }
29341         return diff;
29342     },
29343
29344     // private
29345     onMouseMove : function(e){
29346         
29347         if(this.enabled){
29348             try{// try catch so if something goes wrong the user doesn't get hung
29349
29350             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29351                 return;
29352             }
29353
29354             //var curXY = this.startPoint;
29355             var curSize = this.curSize || this.startBox;
29356             var x = this.startBox.x, y = this.startBox.y;
29357             var ox = x, oy = y;
29358             var w = curSize.width, h = curSize.height;
29359             var ow = w, oh = h;
29360             var mw = this.minWidth, mh = this.minHeight;
29361             var mxw = this.maxWidth, mxh = this.maxHeight;
29362             var wi = this.widthIncrement;
29363             var hi = this.heightIncrement;
29364
29365             var eventXY = e.getXY();
29366             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29367             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29368
29369             var pos = this.activeHandle.position;
29370
29371             switch(pos){
29372                 case "east":
29373                     w += diffX;
29374                     w = Math.min(Math.max(mw, w), mxw);
29375                     break;
29376              
29377                 case "south":
29378                     h += diffY;
29379                     h = Math.min(Math.max(mh, h), mxh);
29380                     break;
29381                 case "southeast":
29382                     w += diffX;
29383                     h += diffY;
29384                     w = Math.min(Math.max(mw, w), mxw);
29385                     h = Math.min(Math.max(mh, h), mxh);
29386                     break;
29387                 case "north":
29388                     diffY = this.constrain(h, diffY, mh, mxh);
29389                     y += diffY;
29390                     h -= diffY;
29391                     break;
29392                 case "hdrag":
29393                     
29394                     if (wi) {
29395                         var adiffX = Math.abs(diffX);
29396                         var sub = (adiffX % wi); // how much 
29397                         if (sub > (wi/2)) { // far enough to snap
29398                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29399                         } else {
29400                             // remove difference.. 
29401                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29402                         }
29403                     }
29404                     x += diffX;
29405                     x = Math.max(this.minX, x);
29406                     break;
29407                 case "west":
29408                     diffX = this.constrain(w, diffX, mw, mxw);
29409                     x += diffX;
29410                     w -= diffX;
29411                     break;
29412                 case "northeast":
29413                     w += diffX;
29414                     w = Math.min(Math.max(mw, w), mxw);
29415                     diffY = this.constrain(h, diffY, mh, mxh);
29416                     y += diffY;
29417                     h -= diffY;
29418                     break;
29419                 case "northwest":
29420                     diffX = this.constrain(w, diffX, mw, mxw);
29421                     diffY = this.constrain(h, diffY, mh, mxh);
29422                     y += diffY;
29423                     h -= diffY;
29424                     x += diffX;
29425                     w -= diffX;
29426                     break;
29427                case "southwest":
29428                     diffX = this.constrain(w, diffX, mw, mxw);
29429                     h += diffY;
29430                     h = Math.min(Math.max(mh, h), mxh);
29431                     x += diffX;
29432                     w -= diffX;
29433                     break;
29434             }
29435
29436             var sw = this.snap(w, wi, mw);
29437             var sh = this.snap(h, hi, mh);
29438             if(sw != w || sh != h){
29439                 switch(pos){
29440                     case "northeast":
29441                         y -= sh - h;
29442                     break;
29443                     case "north":
29444                         y -= sh - h;
29445                         break;
29446                     case "southwest":
29447                         x -= sw - w;
29448                     break;
29449                     case "west":
29450                         x -= sw - w;
29451                         break;
29452                     case "northwest":
29453                         x -= sw - w;
29454                         y -= sh - h;
29455                     break;
29456                 }
29457                 w = sw;
29458                 h = sh;
29459             }
29460
29461             if(this.preserveRatio){
29462                 switch(pos){
29463                     case "southeast":
29464                     case "east":
29465                         h = oh * (w/ow);
29466                         h = Math.min(Math.max(mh, h), mxh);
29467                         w = ow * (h/oh);
29468                        break;
29469                     case "south":
29470                         w = ow * (h/oh);
29471                         w = Math.min(Math.max(mw, w), mxw);
29472                         h = oh * (w/ow);
29473                         break;
29474                     case "northeast":
29475                         w = ow * (h/oh);
29476                         w = Math.min(Math.max(mw, w), mxw);
29477                         h = oh * (w/ow);
29478                     break;
29479                     case "north":
29480                         var tw = w;
29481                         w = ow * (h/oh);
29482                         w = Math.min(Math.max(mw, w), mxw);
29483                         h = oh * (w/ow);
29484                         x += (tw - w) / 2;
29485                         break;
29486                     case "southwest":
29487                         h = oh * (w/ow);
29488                         h = Math.min(Math.max(mh, h), mxh);
29489                         var tw = w;
29490                         w = ow * (h/oh);
29491                         x += tw - w;
29492                         break;
29493                     case "west":
29494                         var th = h;
29495                         h = oh * (w/ow);
29496                         h = Math.min(Math.max(mh, h), mxh);
29497                         y += (th - h) / 2;
29498                         var tw = w;
29499                         w = ow * (h/oh);
29500                         x += tw - w;
29501                        break;
29502                     case "northwest":
29503                         var tw = w;
29504                         var th = h;
29505                         h = oh * (w/ow);
29506                         h = Math.min(Math.max(mh, h), mxh);
29507                         w = ow * (h/oh);
29508                         y += th - h;
29509                         x += tw - w;
29510                        break;
29511
29512                 }
29513             }
29514             if (pos == 'hdrag') {
29515                 w = ow;
29516             }
29517             this.proxy.setBounds(x, y, w, h);
29518             if(this.dynamic){
29519                 this.resizeElement();
29520             }
29521             }catch(e){}
29522         }
29523         this.fireEvent("resizing", this, x, y, w, h, e);
29524     },
29525
29526     // private
29527     handleOver : function(){
29528         if(this.enabled){
29529             this.el.addClass("x-resizable-over");
29530         }
29531     },
29532
29533     // private
29534     handleOut : function(){
29535         if(!this.resizing){
29536             this.el.removeClass("x-resizable-over");
29537         }
29538     },
29539
29540     /**
29541      * Returns the element this component is bound to.
29542      * @return {Roo.Element}
29543      */
29544     getEl : function(){
29545         return this.el;
29546     },
29547
29548     /**
29549      * Returns the resizeChild element (or null).
29550      * @return {Roo.Element}
29551      */
29552     getResizeChild : function(){
29553         return this.resizeChild;
29554     },
29555     groupHandler : function()
29556     {
29557         
29558     },
29559     /**
29560      * Destroys this resizable. If the element was wrapped and
29561      * removeEl is not true then the element remains.
29562      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29563      */
29564     destroy : function(removeEl){
29565         this.proxy.remove();
29566         if(this.overlay){
29567             this.overlay.removeAllListeners();
29568             this.overlay.remove();
29569         }
29570         var ps = Roo.Resizable.positions;
29571         for(var k in ps){
29572             if(typeof ps[k] != "function" && this[ps[k]]){
29573                 var h = this[ps[k]];
29574                 h.el.removeAllListeners();
29575                 h.el.remove();
29576             }
29577         }
29578         if(removeEl){
29579             this.el.update("");
29580             this.el.remove();
29581         }
29582     }
29583 });
29584
29585 // private
29586 // hash to map config positions to true positions
29587 Roo.Resizable.positions = {
29588     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29589     hd: "hdrag"
29590 };
29591
29592 // private
29593 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29594     if(!this.tpl){
29595         // only initialize the template if resizable is used
29596         var tpl = Roo.DomHelper.createTemplate(
29597             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29598         );
29599         tpl.compile();
29600         Roo.Resizable.Handle.prototype.tpl = tpl;
29601     }
29602     this.position = pos;
29603     this.rz = rz;
29604     // show north drag fro topdra
29605     var handlepos = pos == 'hdrag' ? 'north' : pos;
29606     
29607     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29608     if (pos == 'hdrag') {
29609         this.el.setStyle('cursor', 'pointer');
29610     }
29611     this.el.unselectable();
29612     if(transparent){
29613         this.el.setOpacity(0);
29614     }
29615     this.el.on("mousedown", this.onMouseDown, this);
29616     if(!disableTrackOver){
29617         this.el.on("mouseover", this.onMouseOver, this);
29618         this.el.on("mouseout", this.onMouseOut, this);
29619     }
29620 };
29621
29622 // private
29623 Roo.Resizable.Handle.prototype = {
29624     afterResize : function(rz){
29625         Roo.log('after?');
29626         // do nothing
29627     },
29628     // private
29629     onMouseDown : function(e){
29630         this.rz.onMouseDown(this, e);
29631     },
29632     // private
29633     onMouseOver : function(e){
29634         this.rz.handleOver(this, e);
29635     },
29636     // private
29637     onMouseOut : function(e){
29638         this.rz.handleOut(this, e);
29639     }
29640 };/*
29641  * Based on:
29642  * Ext JS Library 1.1.1
29643  * Copyright(c) 2006-2007, Ext JS, LLC.
29644  *
29645  * Originally Released Under LGPL - original licence link has changed is not relivant.
29646  *
29647  * Fork - LGPL
29648  * <script type="text/javascript">
29649  */
29650
29651 /**
29652  * @class Roo.Editor
29653  * @extends Roo.Component
29654  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29655  * @constructor
29656  * Create a new Editor
29657  * @param {Roo.form.Field} field The Field object (or descendant)
29658  * @param {Object} config The config object
29659  */
29660 Roo.Editor = function(field, config){
29661     Roo.Editor.superclass.constructor.call(this, config);
29662     this.field = field;
29663     this.addEvents({
29664         /**
29665              * @event beforestartedit
29666              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29667              * false from the handler of this event.
29668              * @param {Editor} this
29669              * @param {Roo.Element} boundEl The underlying element bound to this editor
29670              * @param {Mixed} value The field value being set
29671              */
29672         "beforestartedit" : true,
29673         /**
29674              * @event startedit
29675              * Fires when this editor is displayed
29676              * @param {Roo.Element} boundEl The underlying element bound to this editor
29677              * @param {Mixed} value The starting field value
29678              */
29679         "startedit" : true,
29680         /**
29681              * @event beforecomplete
29682              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29683              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29684              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29685              * event will not fire since no edit actually occurred.
29686              * @param {Editor} this
29687              * @param {Mixed} value The current field value
29688              * @param {Mixed} startValue The original field value
29689              */
29690         "beforecomplete" : true,
29691         /**
29692              * @event complete
29693              * Fires after editing is complete and any changed value has been written to the underlying field.
29694              * @param {Editor} this
29695              * @param {Mixed} value The current field value
29696              * @param {Mixed} startValue The original field value
29697              */
29698         "complete" : true,
29699         /**
29700          * @event specialkey
29701          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29702          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29703          * @param {Roo.form.Field} this
29704          * @param {Roo.EventObject} e The event object
29705          */
29706         "specialkey" : true
29707     });
29708 };
29709
29710 Roo.extend(Roo.Editor, Roo.Component, {
29711     /**
29712      * @cfg {Boolean/String} autosize
29713      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29714      * or "height" to adopt the height only (defaults to false)
29715      */
29716     /**
29717      * @cfg {Boolean} revertInvalid
29718      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29719      * validation fails (defaults to true)
29720      */
29721     /**
29722      * @cfg {Boolean} ignoreNoChange
29723      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29724      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29725      * will never be ignored.
29726      */
29727     /**
29728      * @cfg {Boolean} hideEl
29729      * False to keep the bound element visible while the editor is displayed (defaults to true)
29730      */
29731     /**
29732      * @cfg {Mixed} value
29733      * The data value of the underlying field (defaults to "")
29734      */
29735     value : "",
29736     /**
29737      * @cfg {String} alignment
29738      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29739      */
29740     alignment: "c-c?",
29741     /**
29742      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29743      * for bottom-right shadow (defaults to "frame")
29744      */
29745     shadow : "frame",
29746     /**
29747      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29748      */
29749     constrain : false,
29750     /**
29751      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29752      */
29753     completeOnEnter : false,
29754     /**
29755      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29756      */
29757     cancelOnEsc : false,
29758     /**
29759      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29760      */
29761     updateEl : false,
29762
29763     // private
29764     onRender : function(ct, position){
29765         this.el = new Roo.Layer({
29766             shadow: this.shadow,
29767             cls: "x-editor",
29768             parentEl : ct,
29769             shim : this.shim,
29770             shadowOffset:4,
29771             id: this.id,
29772             constrain: this.constrain
29773         });
29774         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29775         if(this.field.msgTarget != 'title'){
29776             this.field.msgTarget = 'qtip';
29777         }
29778         this.field.render(this.el);
29779         if(Roo.isGecko){
29780             this.field.el.dom.setAttribute('autocomplete', 'off');
29781         }
29782         this.field.on("specialkey", this.onSpecialKey, this);
29783         if(this.swallowKeys){
29784             this.field.el.swallowEvent(['keydown','keypress']);
29785         }
29786         this.field.show();
29787         this.field.on("blur", this.onBlur, this);
29788         if(this.field.grow){
29789             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29790         }
29791     },
29792
29793     onSpecialKey : function(field, e)
29794     {
29795         //Roo.log('editor onSpecialKey');
29796         if(this.completeOnEnter && e.getKey() == e.ENTER){
29797             e.stopEvent();
29798             this.completeEdit();
29799             return;
29800         }
29801         // do not fire special key otherwise it might hide close the editor...
29802         if(e.getKey() == e.ENTER){    
29803             return;
29804         }
29805         if(this.cancelOnEsc && e.getKey() == e.ESC){
29806             this.cancelEdit();
29807             return;
29808         } 
29809         this.fireEvent('specialkey', field, e);
29810     
29811     },
29812
29813     /**
29814      * Starts the editing process and shows the editor.
29815      * @param {String/HTMLElement/Element} el The element to edit
29816      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29817       * to the innerHTML of el.
29818      */
29819     startEdit : function(el, value){
29820         if(this.editing){
29821             this.completeEdit();
29822         }
29823         this.boundEl = Roo.get(el);
29824         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29825         if(!this.rendered){
29826             this.render(this.parentEl || document.body);
29827         }
29828         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29829             return;
29830         }
29831         this.startValue = v;
29832         this.field.setValue(v);
29833         if(this.autoSize){
29834             var sz = this.boundEl.getSize();
29835             switch(this.autoSize){
29836                 case "width":
29837                 this.setSize(sz.width,  "");
29838                 break;
29839                 case "height":
29840                 this.setSize("",  sz.height);
29841                 break;
29842                 default:
29843                 this.setSize(sz.width,  sz.height);
29844             }
29845         }
29846         this.el.alignTo(this.boundEl, this.alignment);
29847         this.editing = true;
29848         if(Roo.QuickTips){
29849             Roo.QuickTips.disable();
29850         }
29851         this.show();
29852     },
29853
29854     /**
29855      * Sets the height and width of this editor.
29856      * @param {Number} width The new width
29857      * @param {Number} height The new height
29858      */
29859     setSize : function(w, h){
29860         this.field.setSize(w, h);
29861         if(this.el){
29862             this.el.sync();
29863         }
29864     },
29865
29866     /**
29867      * Realigns the editor to the bound field based on the current alignment config value.
29868      */
29869     realign : function(){
29870         this.el.alignTo(this.boundEl, this.alignment);
29871     },
29872
29873     /**
29874      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29875      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29876      */
29877     completeEdit : function(remainVisible){
29878         if(!this.editing){
29879             return;
29880         }
29881         var v = this.getValue();
29882         if(this.revertInvalid !== false && !this.field.isValid()){
29883             v = this.startValue;
29884             this.cancelEdit(true);
29885         }
29886         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29887             this.editing = false;
29888             this.hide();
29889             return;
29890         }
29891         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29892             this.editing = false;
29893             if(this.updateEl && this.boundEl){
29894                 this.boundEl.update(v);
29895             }
29896             if(remainVisible !== true){
29897                 this.hide();
29898             }
29899             this.fireEvent("complete", this, v, this.startValue);
29900         }
29901     },
29902
29903     // private
29904     onShow : function(){
29905         this.el.show();
29906         if(this.hideEl !== false){
29907             this.boundEl.hide();
29908         }
29909         this.field.show();
29910         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29911             this.fixIEFocus = true;
29912             this.deferredFocus.defer(50, this);
29913         }else{
29914             this.field.focus();
29915         }
29916         this.fireEvent("startedit", this.boundEl, this.startValue);
29917     },
29918
29919     deferredFocus : function(){
29920         if(this.editing){
29921             this.field.focus();
29922         }
29923     },
29924
29925     /**
29926      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29927      * reverted to the original starting value.
29928      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29929      * cancel (defaults to false)
29930      */
29931     cancelEdit : function(remainVisible){
29932         if(this.editing){
29933             this.setValue(this.startValue);
29934             if(remainVisible !== true){
29935                 this.hide();
29936             }
29937         }
29938     },
29939
29940     // private
29941     onBlur : function(){
29942         if(this.allowBlur !== true && this.editing){
29943             this.completeEdit();
29944         }
29945     },
29946
29947     // private
29948     onHide : function(){
29949         if(this.editing){
29950             this.completeEdit();
29951             return;
29952         }
29953         this.field.blur();
29954         if(this.field.collapse){
29955             this.field.collapse();
29956         }
29957         this.el.hide();
29958         if(this.hideEl !== false){
29959             this.boundEl.show();
29960         }
29961         if(Roo.QuickTips){
29962             Roo.QuickTips.enable();
29963         }
29964     },
29965
29966     /**
29967      * Sets the data value of the editor
29968      * @param {Mixed} value Any valid value supported by the underlying field
29969      */
29970     setValue : function(v){
29971         this.field.setValue(v);
29972     },
29973
29974     /**
29975      * Gets the data value of the editor
29976      * @return {Mixed} The data value
29977      */
29978     getValue : function(){
29979         return this.field.getValue();
29980     }
29981 });/*
29982  * Based on:
29983  * Ext JS Library 1.1.1
29984  * Copyright(c) 2006-2007, Ext JS, LLC.
29985  *
29986  * Originally Released Under LGPL - original licence link has changed is not relivant.
29987  *
29988  * Fork - LGPL
29989  * <script type="text/javascript">
29990  */
29991  
29992 /**
29993  * @class Roo.BasicDialog
29994  * @extends Roo.util.Observable
29995  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29996  * <pre><code>
29997 var dlg = new Roo.BasicDialog("my-dlg", {
29998     height: 200,
29999     width: 300,
30000     minHeight: 100,
30001     minWidth: 150,
30002     modal: true,
30003     proxyDrag: true,
30004     shadow: true
30005 });
30006 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30007 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30008 dlg.addButton('Cancel', dlg.hide, dlg);
30009 dlg.show();
30010 </code></pre>
30011   <b>A Dialog should always be a direct child of the body element.</b>
30012  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30013  * @cfg {String} title Default text to display in the title bar (defaults to null)
30014  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30015  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30016  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30017  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30018  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30019  * (defaults to null with no animation)
30020  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30021  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30022  * property for valid values (defaults to 'all')
30023  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30024  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30025  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30026  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30027  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30028  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30029  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30030  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30031  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30032  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30033  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30034  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30035  * draggable = true (defaults to false)
30036  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30037  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30038  * shadow (defaults to false)
30039  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30040  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30041  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30042  * @cfg {Array} buttons Array of buttons
30043  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30044  * @constructor
30045  * Create a new BasicDialog.
30046  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30047  * @param {Object} config Configuration options
30048  */
30049 Roo.BasicDialog = function(el, config){
30050     this.el = Roo.get(el);
30051     var dh = Roo.DomHelper;
30052     if(!this.el && config && config.autoCreate){
30053         if(typeof config.autoCreate == "object"){
30054             if(!config.autoCreate.id){
30055                 config.autoCreate.id = el;
30056             }
30057             this.el = dh.append(document.body,
30058                         config.autoCreate, true);
30059         }else{
30060             this.el = dh.append(document.body,
30061                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30062         }
30063     }
30064     el = this.el;
30065     el.setDisplayed(true);
30066     el.hide = this.hideAction;
30067     this.id = el.id;
30068     el.addClass("x-dlg");
30069
30070     Roo.apply(this, config);
30071
30072     this.proxy = el.createProxy("x-dlg-proxy");
30073     this.proxy.hide = this.hideAction;
30074     this.proxy.setOpacity(.5);
30075     this.proxy.hide();
30076
30077     if(config.width){
30078         el.setWidth(config.width);
30079     }
30080     if(config.height){
30081         el.setHeight(config.height);
30082     }
30083     this.size = el.getSize();
30084     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30085         this.xy = [config.x,config.y];
30086     }else{
30087         this.xy = el.getCenterXY(true);
30088     }
30089     /** The header element @type Roo.Element */
30090     this.header = el.child("> .x-dlg-hd");
30091     /** The body element @type Roo.Element */
30092     this.body = el.child("> .x-dlg-bd");
30093     /** The footer element @type Roo.Element */
30094     this.footer = el.child("> .x-dlg-ft");
30095
30096     if(!this.header){
30097         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30098     }
30099     if(!this.body){
30100         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30101     }
30102
30103     this.header.unselectable();
30104     if(this.title){
30105         this.header.update(this.title);
30106     }
30107     // this element allows the dialog to be focused for keyboard event
30108     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30109     this.focusEl.swallowEvent("click", true);
30110
30111     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30112
30113     // wrap the body and footer for special rendering
30114     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30115     if(this.footer){
30116         this.bwrap.dom.appendChild(this.footer.dom);
30117     }
30118
30119     this.bg = this.el.createChild({
30120         tag: "div", cls:"x-dlg-bg",
30121         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30122     });
30123     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30124
30125
30126     if(this.autoScroll !== false && !this.autoTabs){
30127         this.body.setStyle("overflow", "auto");
30128     }
30129
30130     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30131
30132     if(this.closable !== false){
30133         this.el.addClass("x-dlg-closable");
30134         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30135         this.close.on("click", this.closeClick, this);
30136         this.close.addClassOnOver("x-dlg-close-over");
30137     }
30138     if(this.collapsible !== false){
30139         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30140         this.collapseBtn.on("click", this.collapseClick, this);
30141         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30142         this.header.on("dblclick", this.collapseClick, this);
30143     }
30144     if(this.resizable !== false){
30145         this.el.addClass("x-dlg-resizable");
30146         this.resizer = new Roo.Resizable(el, {
30147             minWidth: this.minWidth || 80,
30148             minHeight:this.minHeight || 80,
30149             handles: this.resizeHandles || "all",
30150             pinned: true
30151         });
30152         this.resizer.on("beforeresize", this.beforeResize, this);
30153         this.resizer.on("resize", this.onResize, this);
30154     }
30155     if(this.draggable !== false){
30156         el.addClass("x-dlg-draggable");
30157         if (!this.proxyDrag) {
30158             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30159         }
30160         else {
30161             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30162         }
30163         dd.setHandleElId(this.header.id);
30164         dd.endDrag = this.endMove.createDelegate(this);
30165         dd.startDrag = this.startMove.createDelegate(this);
30166         dd.onDrag = this.onDrag.createDelegate(this);
30167         dd.scroll = false;
30168         this.dd = dd;
30169     }
30170     if(this.modal){
30171         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30172         this.mask.enableDisplayMode("block");
30173         this.mask.hide();
30174         this.el.addClass("x-dlg-modal");
30175     }
30176     if(this.shadow){
30177         this.shadow = new Roo.Shadow({
30178             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30179             offset : this.shadowOffset
30180         });
30181     }else{
30182         this.shadowOffset = 0;
30183     }
30184     if(Roo.useShims && this.shim !== false){
30185         this.shim = this.el.createShim();
30186         this.shim.hide = this.hideAction;
30187         this.shim.hide();
30188     }else{
30189         this.shim = false;
30190     }
30191     if(this.autoTabs){
30192         this.initTabs();
30193     }
30194     if (this.buttons) { 
30195         var bts= this.buttons;
30196         this.buttons = [];
30197         Roo.each(bts, function(b) {
30198             this.addButton(b);
30199         }, this);
30200     }
30201     
30202     
30203     this.addEvents({
30204         /**
30205          * @event keydown
30206          * Fires when a key is pressed
30207          * @param {Roo.BasicDialog} this
30208          * @param {Roo.EventObject} e
30209          */
30210         "keydown" : true,
30211         /**
30212          * @event move
30213          * Fires when this dialog is moved by the user.
30214          * @param {Roo.BasicDialog} this
30215          * @param {Number} x The new page X
30216          * @param {Number} y The new page Y
30217          */
30218         "move" : true,
30219         /**
30220          * @event resize
30221          * Fires when this dialog is resized by the user.
30222          * @param {Roo.BasicDialog} this
30223          * @param {Number} width The new width
30224          * @param {Number} height The new height
30225          */
30226         "resize" : true,
30227         /**
30228          * @event beforehide
30229          * Fires before this dialog is hidden.
30230          * @param {Roo.BasicDialog} this
30231          */
30232         "beforehide" : true,
30233         /**
30234          * @event hide
30235          * Fires when this dialog is hidden.
30236          * @param {Roo.BasicDialog} this
30237          */
30238         "hide" : true,
30239         /**
30240          * @event beforeshow
30241          * Fires before this dialog is shown.
30242          * @param {Roo.BasicDialog} this
30243          */
30244         "beforeshow" : true,
30245         /**
30246          * @event show
30247          * Fires when this dialog is shown.
30248          * @param {Roo.BasicDialog} this
30249          */
30250         "show" : true
30251     });
30252     el.on("keydown", this.onKeyDown, this);
30253     el.on("mousedown", this.toFront, this);
30254     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30255     this.el.hide();
30256     Roo.DialogManager.register(this);
30257     Roo.BasicDialog.superclass.constructor.call(this);
30258 };
30259
30260 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30261     shadowOffset: Roo.isIE ? 6 : 5,
30262     minHeight: 80,
30263     minWidth: 200,
30264     minButtonWidth: 75,
30265     defaultButton: null,
30266     buttonAlign: "right",
30267     tabTag: 'div',
30268     firstShow: true,
30269
30270     /**
30271      * Sets the dialog title text
30272      * @param {String} text The title text to display
30273      * @return {Roo.BasicDialog} this
30274      */
30275     setTitle : function(text){
30276         this.header.update(text);
30277         return this;
30278     },
30279
30280     // private
30281     closeClick : function(){
30282         this.hide();
30283     },
30284
30285     // private
30286     collapseClick : function(){
30287         this[this.collapsed ? "expand" : "collapse"]();
30288     },
30289
30290     /**
30291      * Collapses the dialog to its minimized state (only the title bar is visible).
30292      * Equivalent to the user clicking the collapse dialog button.
30293      */
30294     collapse : function(){
30295         if(!this.collapsed){
30296             this.collapsed = true;
30297             this.el.addClass("x-dlg-collapsed");
30298             this.restoreHeight = this.el.getHeight();
30299             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30300         }
30301     },
30302
30303     /**
30304      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30305      * clicking the expand dialog button.
30306      */
30307     expand : function(){
30308         if(this.collapsed){
30309             this.collapsed = false;
30310             this.el.removeClass("x-dlg-collapsed");
30311             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30312         }
30313     },
30314
30315     /**
30316      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30317      * @return {Roo.TabPanel} The tabs component
30318      */
30319     initTabs : function(){
30320         var tabs = this.getTabs();
30321         while(tabs.getTab(0)){
30322             tabs.removeTab(0);
30323         }
30324         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30325             var dom = el.dom;
30326             tabs.addTab(Roo.id(dom), dom.title);
30327             dom.title = "";
30328         });
30329         tabs.activate(0);
30330         return tabs;
30331     },
30332
30333     // private
30334     beforeResize : function(){
30335         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30336     },
30337
30338     // private
30339     onResize : function(){
30340         this.refreshSize();
30341         this.syncBodyHeight();
30342         this.adjustAssets();
30343         this.focus();
30344         this.fireEvent("resize", this, this.size.width, this.size.height);
30345     },
30346
30347     // private
30348     onKeyDown : function(e){
30349         if(this.isVisible()){
30350             this.fireEvent("keydown", this, e);
30351         }
30352     },
30353
30354     /**
30355      * Resizes the dialog.
30356      * @param {Number} width
30357      * @param {Number} height
30358      * @return {Roo.BasicDialog} this
30359      */
30360     resizeTo : function(width, height){
30361         this.el.setSize(width, height);
30362         this.size = {width: width, height: height};
30363         this.syncBodyHeight();
30364         if(this.fixedcenter){
30365             this.center();
30366         }
30367         if(this.isVisible()){
30368             this.constrainXY();
30369             this.adjustAssets();
30370         }
30371         this.fireEvent("resize", this, width, height);
30372         return this;
30373     },
30374
30375
30376     /**
30377      * Resizes the dialog to fit the specified content size.
30378      * @param {Number} width
30379      * @param {Number} height
30380      * @return {Roo.BasicDialog} this
30381      */
30382     setContentSize : function(w, h){
30383         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30384         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30385         //if(!this.el.isBorderBox()){
30386             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30387             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30388         //}
30389         if(this.tabs){
30390             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30391             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30392         }
30393         this.resizeTo(w, h);
30394         return this;
30395     },
30396
30397     /**
30398      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30399      * executed in response to a particular key being pressed while the dialog is active.
30400      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30401      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30402      * @param {Function} fn The function to call
30403      * @param {Object} scope (optional) The scope of the function
30404      * @return {Roo.BasicDialog} this
30405      */
30406     addKeyListener : function(key, fn, scope){
30407         var keyCode, shift, ctrl, alt;
30408         if(typeof key == "object" && !(key instanceof Array)){
30409             keyCode = key["key"];
30410             shift = key["shift"];
30411             ctrl = key["ctrl"];
30412             alt = key["alt"];
30413         }else{
30414             keyCode = key;
30415         }
30416         var handler = function(dlg, e){
30417             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30418                 var k = e.getKey();
30419                 if(keyCode instanceof Array){
30420                     for(var i = 0, len = keyCode.length; i < len; i++){
30421                         if(keyCode[i] == k){
30422                           fn.call(scope || window, dlg, k, e);
30423                           return;
30424                         }
30425                     }
30426                 }else{
30427                     if(k == keyCode){
30428                         fn.call(scope || window, dlg, k, e);
30429                     }
30430                 }
30431             }
30432         };
30433         this.on("keydown", handler);
30434         return this;
30435     },
30436
30437     /**
30438      * Returns the TabPanel component (creates it if it doesn't exist).
30439      * Note: If you wish to simply check for the existence of tabs without creating them,
30440      * check for a null 'tabs' property.
30441      * @return {Roo.TabPanel} The tabs component
30442      */
30443     getTabs : function(){
30444         if(!this.tabs){
30445             this.el.addClass("x-dlg-auto-tabs");
30446             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30447             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30448         }
30449         return this.tabs;
30450     },
30451
30452     /**
30453      * Adds a button to the footer section of the dialog.
30454      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30455      * object or a valid Roo.DomHelper element config
30456      * @param {Function} handler The function called when the button is clicked
30457      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30458      * @return {Roo.Button} The new button
30459      */
30460     addButton : function(config, handler, scope){
30461         var dh = Roo.DomHelper;
30462         if(!this.footer){
30463             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30464         }
30465         if(!this.btnContainer){
30466             var tb = this.footer.createChild({
30467
30468                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30469                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30470             }, null, true);
30471             this.btnContainer = tb.firstChild.firstChild.firstChild;
30472         }
30473         var bconfig = {
30474             handler: handler,
30475             scope: scope,
30476             minWidth: this.minButtonWidth,
30477             hideParent:true
30478         };
30479         if(typeof config == "string"){
30480             bconfig.text = config;
30481         }else{
30482             if(config.tag){
30483                 bconfig.dhconfig = config;
30484             }else{
30485                 Roo.apply(bconfig, config);
30486             }
30487         }
30488         var fc = false;
30489         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30490             bconfig.position = Math.max(0, bconfig.position);
30491             fc = this.btnContainer.childNodes[bconfig.position];
30492         }
30493          
30494         var btn = new Roo.Button(
30495             fc ? 
30496                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30497                 : this.btnContainer.appendChild(document.createElement("td")),
30498             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30499             bconfig
30500         );
30501         this.syncBodyHeight();
30502         if(!this.buttons){
30503             /**
30504              * Array of all the buttons that have been added to this dialog via addButton
30505              * @type Array
30506              */
30507             this.buttons = [];
30508         }
30509         this.buttons.push(btn);
30510         return btn;
30511     },
30512
30513     /**
30514      * Sets the default button to be focused when the dialog is displayed.
30515      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30516      * @return {Roo.BasicDialog} this
30517      */
30518     setDefaultButton : function(btn){
30519         this.defaultButton = btn;
30520         return this;
30521     },
30522
30523     // private
30524     getHeaderFooterHeight : function(safe){
30525         var height = 0;
30526         if(this.header){
30527            height += this.header.getHeight();
30528         }
30529         if(this.footer){
30530            var fm = this.footer.getMargins();
30531             height += (this.footer.getHeight()+fm.top+fm.bottom);
30532         }
30533         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30534         height += this.centerBg.getPadding("tb");
30535         return height;
30536     },
30537
30538     // private
30539     syncBodyHeight : function()
30540     {
30541         var bd = this.body, // the text
30542             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30543             bw = this.bwrap;
30544         var height = this.size.height - this.getHeaderFooterHeight(false);
30545         bd.setHeight(height-bd.getMargins("tb"));
30546         var hh = this.header.getHeight();
30547         var h = this.size.height-hh;
30548         cb.setHeight(h);
30549         
30550         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30551         bw.setHeight(h-cb.getPadding("tb"));
30552         
30553         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30554         bd.setWidth(bw.getWidth(true));
30555         if(this.tabs){
30556             this.tabs.syncHeight();
30557             if(Roo.isIE){
30558                 this.tabs.el.repaint();
30559             }
30560         }
30561     },
30562
30563     /**
30564      * Restores the previous state of the dialog if Roo.state is configured.
30565      * @return {Roo.BasicDialog} this
30566      */
30567     restoreState : function(){
30568         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30569         if(box && box.width){
30570             this.xy = [box.x, box.y];
30571             this.resizeTo(box.width, box.height);
30572         }
30573         return this;
30574     },
30575
30576     // private
30577     beforeShow : function(){
30578         this.expand();
30579         if(this.fixedcenter){
30580             this.xy = this.el.getCenterXY(true);
30581         }
30582         if(this.modal){
30583             Roo.get(document.body).addClass("x-body-masked");
30584             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30585             this.mask.show();
30586         }
30587         this.constrainXY();
30588     },
30589
30590     // private
30591     animShow : function(){
30592         var b = Roo.get(this.animateTarget).getBox();
30593         this.proxy.setSize(b.width, b.height);
30594         this.proxy.setLocation(b.x, b.y);
30595         this.proxy.show();
30596         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30597                     true, .35, this.showEl.createDelegate(this));
30598     },
30599
30600     /**
30601      * Shows the dialog.
30602      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30603      * @return {Roo.BasicDialog} this
30604      */
30605     show : function(animateTarget){
30606         if (this.fireEvent("beforeshow", this) === false){
30607             return;
30608         }
30609         if(this.syncHeightBeforeShow){
30610             this.syncBodyHeight();
30611         }else if(this.firstShow){
30612             this.firstShow = false;
30613             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30614         }
30615         this.animateTarget = animateTarget || this.animateTarget;
30616         if(!this.el.isVisible()){
30617             this.beforeShow();
30618             if(this.animateTarget && Roo.get(this.animateTarget)){
30619                 this.animShow();
30620             }else{
30621                 this.showEl();
30622             }
30623         }
30624         return this;
30625     },
30626
30627     // private
30628     showEl : function(){
30629         this.proxy.hide();
30630         this.el.setXY(this.xy);
30631         this.el.show();
30632         this.adjustAssets(true);
30633         this.toFront();
30634         this.focus();
30635         // IE peekaboo bug - fix found by Dave Fenwick
30636         if(Roo.isIE){
30637             this.el.repaint();
30638         }
30639         this.fireEvent("show", this);
30640     },
30641
30642     /**
30643      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30644      * dialog itself will receive focus.
30645      */
30646     focus : function(){
30647         if(this.defaultButton){
30648             this.defaultButton.focus();
30649         }else{
30650             this.focusEl.focus();
30651         }
30652     },
30653
30654     // private
30655     constrainXY : function(){
30656         if(this.constraintoviewport !== false){
30657             if(!this.viewSize){
30658                 if(this.container){
30659                     var s = this.container.getSize();
30660                     this.viewSize = [s.width, s.height];
30661                 }else{
30662                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30663                 }
30664             }
30665             var s = Roo.get(this.container||document).getScroll();
30666
30667             var x = this.xy[0], y = this.xy[1];
30668             var w = this.size.width, h = this.size.height;
30669             var vw = this.viewSize[0], vh = this.viewSize[1];
30670             // only move it if it needs it
30671             var moved = false;
30672             // first validate right/bottom
30673             if(x + w > vw+s.left){
30674                 x = vw - w;
30675                 moved = true;
30676             }
30677             if(y + h > vh+s.top){
30678                 y = vh - h;
30679                 moved = true;
30680             }
30681             // then make sure top/left isn't negative
30682             if(x < s.left){
30683                 x = s.left;
30684                 moved = true;
30685             }
30686             if(y < s.top){
30687                 y = s.top;
30688                 moved = true;
30689             }
30690             if(moved){
30691                 // cache xy
30692                 this.xy = [x, y];
30693                 if(this.isVisible()){
30694                     this.el.setLocation(x, y);
30695                     this.adjustAssets();
30696                 }
30697             }
30698         }
30699     },
30700
30701     // private
30702     onDrag : function(){
30703         if(!this.proxyDrag){
30704             this.xy = this.el.getXY();
30705             this.adjustAssets();
30706         }
30707     },
30708
30709     // private
30710     adjustAssets : function(doShow){
30711         var x = this.xy[0], y = this.xy[1];
30712         var w = this.size.width, h = this.size.height;
30713         if(doShow === true){
30714             if(this.shadow){
30715                 this.shadow.show(this.el);
30716             }
30717             if(this.shim){
30718                 this.shim.show();
30719             }
30720         }
30721         if(this.shadow && this.shadow.isVisible()){
30722             this.shadow.show(this.el);
30723         }
30724         if(this.shim && this.shim.isVisible()){
30725             this.shim.setBounds(x, y, w, h);
30726         }
30727     },
30728
30729     // private
30730     adjustViewport : function(w, h){
30731         if(!w || !h){
30732             w = Roo.lib.Dom.getViewWidth();
30733             h = Roo.lib.Dom.getViewHeight();
30734         }
30735         // cache the size
30736         this.viewSize = [w, h];
30737         if(this.modal && this.mask.isVisible()){
30738             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30739             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30740         }
30741         if(this.isVisible()){
30742             this.constrainXY();
30743         }
30744     },
30745
30746     /**
30747      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30748      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30749      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30750      */
30751     destroy : function(removeEl){
30752         if(this.isVisible()){
30753             this.animateTarget = null;
30754             this.hide();
30755         }
30756         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30757         if(this.tabs){
30758             this.tabs.destroy(removeEl);
30759         }
30760         Roo.destroy(
30761              this.shim,
30762              this.proxy,
30763              this.resizer,
30764              this.close,
30765              this.mask
30766         );
30767         if(this.dd){
30768             this.dd.unreg();
30769         }
30770         if(this.buttons){
30771            for(var i = 0, len = this.buttons.length; i < len; i++){
30772                this.buttons[i].destroy();
30773            }
30774         }
30775         this.el.removeAllListeners();
30776         if(removeEl === true){
30777             this.el.update("");
30778             this.el.remove();
30779         }
30780         Roo.DialogManager.unregister(this);
30781     },
30782
30783     // private
30784     startMove : function(){
30785         if(this.proxyDrag){
30786             this.proxy.show();
30787         }
30788         if(this.constraintoviewport !== false){
30789             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30790         }
30791     },
30792
30793     // private
30794     endMove : function(){
30795         if(!this.proxyDrag){
30796             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30797         }else{
30798             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30799             this.proxy.hide();
30800         }
30801         this.refreshSize();
30802         this.adjustAssets();
30803         this.focus();
30804         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30805     },
30806
30807     /**
30808      * Brings this dialog to the front of any other visible dialogs
30809      * @return {Roo.BasicDialog} this
30810      */
30811     toFront : function(){
30812         Roo.DialogManager.bringToFront(this);
30813         return this;
30814     },
30815
30816     /**
30817      * Sends this dialog to the back (under) of any other visible dialogs
30818      * @return {Roo.BasicDialog} this
30819      */
30820     toBack : function(){
30821         Roo.DialogManager.sendToBack(this);
30822         return this;
30823     },
30824
30825     /**
30826      * Centers this dialog in the viewport
30827      * @return {Roo.BasicDialog} this
30828      */
30829     center : function(){
30830         var xy = this.el.getCenterXY(true);
30831         this.moveTo(xy[0], xy[1]);
30832         return this;
30833     },
30834
30835     /**
30836      * Moves the dialog's top-left corner to the specified point
30837      * @param {Number} x
30838      * @param {Number} y
30839      * @return {Roo.BasicDialog} this
30840      */
30841     moveTo : function(x, y){
30842         this.xy = [x,y];
30843         if(this.isVisible()){
30844             this.el.setXY(this.xy);
30845             this.adjustAssets();
30846         }
30847         return this;
30848     },
30849
30850     /**
30851      * Aligns the dialog to the specified element
30852      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30853      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30854      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30855      * @return {Roo.BasicDialog} this
30856      */
30857     alignTo : function(element, position, offsets){
30858         this.xy = this.el.getAlignToXY(element, position, offsets);
30859         if(this.isVisible()){
30860             this.el.setXY(this.xy);
30861             this.adjustAssets();
30862         }
30863         return this;
30864     },
30865
30866     /**
30867      * Anchors an element to another element and realigns it when the window is resized.
30868      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30869      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30870      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30871      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30872      * is a number, it is used as the buffer delay (defaults to 50ms).
30873      * @return {Roo.BasicDialog} this
30874      */
30875     anchorTo : function(el, alignment, offsets, monitorScroll){
30876         var action = function(){
30877             this.alignTo(el, alignment, offsets);
30878         };
30879         Roo.EventManager.onWindowResize(action, this);
30880         var tm = typeof monitorScroll;
30881         if(tm != 'undefined'){
30882             Roo.EventManager.on(window, 'scroll', action, this,
30883                 {buffer: tm == 'number' ? monitorScroll : 50});
30884         }
30885         action.call(this);
30886         return this;
30887     },
30888
30889     /**
30890      * Returns true if the dialog is visible
30891      * @return {Boolean}
30892      */
30893     isVisible : function(){
30894         return this.el.isVisible();
30895     },
30896
30897     // private
30898     animHide : function(callback){
30899         var b = Roo.get(this.animateTarget).getBox();
30900         this.proxy.show();
30901         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30902         this.el.hide();
30903         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30904                     this.hideEl.createDelegate(this, [callback]));
30905     },
30906
30907     /**
30908      * Hides the dialog.
30909      * @param {Function} callback (optional) Function to call when the dialog is hidden
30910      * @return {Roo.BasicDialog} this
30911      */
30912     hide : function(callback){
30913         if (this.fireEvent("beforehide", this) === false){
30914             return;
30915         }
30916         if(this.shadow){
30917             this.shadow.hide();
30918         }
30919         if(this.shim) {
30920           this.shim.hide();
30921         }
30922         // sometimes animateTarget seems to get set.. causing problems...
30923         // this just double checks..
30924         if(this.animateTarget && Roo.get(this.animateTarget)) {
30925            this.animHide(callback);
30926         }else{
30927             this.el.hide();
30928             this.hideEl(callback);
30929         }
30930         return this;
30931     },
30932
30933     // private
30934     hideEl : function(callback){
30935         this.proxy.hide();
30936         if(this.modal){
30937             this.mask.hide();
30938             Roo.get(document.body).removeClass("x-body-masked");
30939         }
30940         this.fireEvent("hide", this);
30941         if(typeof callback == "function"){
30942             callback();
30943         }
30944     },
30945
30946     // private
30947     hideAction : function(){
30948         this.setLeft("-10000px");
30949         this.setTop("-10000px");
30950         this.setStyle("visibility", "hidden");
30951     },
30952
30953     // private
30954     refreshSize : function(){
30955         this.size = this.el.getSize();
30956         this.xy = this.el.getXY();
30957         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30958     },
30959
30960     // private
30961     // z-index is managed by the DialogManager and may be overwritten at any time
30962     setZIndex : function(index){
30963         if(this.modal){
30964             this.mask.setStyle("z-index", index);
30965         }
30966         if(this.shim){
30967             this.shim.setStyle("z-index", ++index);
30968         }
30969         if(this.shadow){
30970             this.shadow.setZIndex(++index);
30971         }
30972         this.el.setStyle("z-index", ++index);
30973         if(this.proxy){
30974             this.proxy.setStyle("z-index", ++index);
30975         }
30976         if(this.resizer){
30977             this.resizer.proxy.setStyle("z-index", ++index);
30978         }
30979
30980         this.lastZIndex = index;
30981     },
30982
30983     /**
30984      * Returns the element for this dialog
30985      * @return {Roo.Element} The underlying dialog Element
30986      */
30987     getEl : function(){
30988         return this.el;
30989     }
30990 });
30991
30992 /**
30993  * @class Roo.DialogManager
30994  * Provides global access to BasicDialogs that have been created and
30995  * support for z-indexing (layering) multiple open dialogs.
30996  */
30997 Roo.DialogManager = function(){
30998     var list = {};
30999     var accessList = [];
31000     var front = null;
31001
31002     // private
31003     var sortDialogs = function(d1, d2){
31004         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31005     };
31006
31007     // private
31008     var orderDialogs = function(){
31009         accessList.sort(sortDialogs);
31010         var seed = Roo.DialogManager.zseed;
31011         for(var i = 0, len = accessList.length; i < len; i++){
31012             var dlg = accessList[i];
31013             if(dlg){
31014                 dlg.setZIndex(seed + (i*10));
31015             }
31016         }
31017     };
31018
31019     return {
31020         /**
31021          * The starting z-index for BasicDialogs (defaults to 9000)
31022          * @type Number The z-index value
31023          */
31024         zseed : 9000,
31025
31026         // private
31027         register : function(dlg){
31028             list[dlg.id] = dlg;
31029             accessList.push(dlg);
31030         },
31031
31032         // private
31033         unregister : function(dlg){
31034             delete list[dlg.id];
31035             var i=0;
31036             var len=0;
31037             if(!accessList.indexOf){
31038                 for(  i = 0, len = accessList.length; i < len; i++){
31039                     if(accessList[i] == dlg){
31040                         accessList.splice(i, 1);
31041                         return;
31042                     }
31043                 }
31044             }else{
31045                  i = accessList.indexOf(dlg);
31046                 if(i != -1){
31047                     accessList.splice(i, 1);
31048                 }
31049             }
31050         },
31051
31052         /**
31053          * Gets a registered dialog by id
31054          * @param {String/Object} id The id of the dialog or a dialog
31055          * @return {Roo.BasicDialog} this
31056          */
31057         get : function(id){
31058             return typeof id == "object" ? id : list[id];
31059         },
31060
31061         /**
31062          * Brings the specified dialog to the front
31063          * @param {String/Object} dlg The id of the dialog or a dialog
31064          * @return {Roo.BasicDialog} this
31065          */
31066         bringToFront : function(dlg){
31067             dlg = this.get(dlg);
31068             if(dlg != front){
31069                 front = dlg;
31070                 dlg._lastAccess = new Date().getTime();
31071                 orderDialogs();
31072             }
31073             return dlg;
31074         },
31075
31076         /**
31077          * Sends the specified dialog to the back
31078          * @param {String/Object} dlg The id of the dialog or a dialog
31079          * @return {Roo.BasicDialog} this
31080          */
31081         sendToBack : function(dlg){
31082             dlg = this.get(dlg);
31083             dlg._lastAccess = -(new Date().getTime());
31084             orderDialogs();
31085             return dlg;
31086         },
31087
31088         /**
31089          * Hides all dialogs
31090          */
31091         hideAll : function(){
31092             for(var id in list){
31093                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31094                     list[id].hide();
31095                 }
31096             }
31097         }
31098     };
31099 }();
31100
31101 /**
31102  * @class Roo.LayoutDialog
31103  * @extends Roo.BasicDialog
31104  * Dialog which provides adjustments for working with a layout in a Dialog.
31105  * Add your necessary layout config options to the dialog's config.<br>
31106  * Example usage (including a nested layout):
31107  * <pre><code>
31108 if(!dialog){
31109     dialog = new Roo.LayoutDialog("download-dlg", {
31110         modal: true,
31111         width:600,
31112         height:450,
31113         shadow:true,
31114         minWidth:500,
31115         minHeight:350,
31116         autoTabs:true,
31117         proxyDrag:true,
31118         // layout config merges with the dialog config
31119         center:{
31120             tabPosition: "top",
31121             alwaysShowTabs: true
31122         }
31123     });
31124     dialog.addKeyListener(27, dialog.hide, dialog);
31125     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31126     dialog.addButton("Build It!", this.getDownload, this);
31127
31128     // we can even add nested layouts
31129     var innerLayout = new Roo.BorderLayout("dl-inner", {
31130         east: {
31131             initialSize: 200,
31132             autoScroll:true,
31133             split:true
31134         },
31135         center: {
31136             autoScroll:true
31137         }
31138     });
31139     innerLayout.beginUpdate();
31140     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31141     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31142     innerLayout.endUpdate(true);
31143
31144     var layout = dialog.getLayout();
31145     layout.beginUpdate();
31146     layout.add("center", new Roo.ContentPanel("standard-panel",
31147                         {title: "Download the Source", fitToFrame:true}));
31148     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31149                {title: "Build your own roo.js"}));
31150     layout.getRegion("center").showPanel(sp);
31151     layout.endUpdate();
31152 }
31153 </code></pre>
31154     * @constructor
31155     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31156     * @param {Object} config configuration options
31157   */
31158 Roo.LayoutDialog = function(el, cfg){
31159     
31160     var config=  cfg;
31161     if (typeof(cfg) == 'undefined') {
31162         config = Roo.apply({}, el);
31163         // not sure why we use documentElement here.. - it should always be body.
31164         // IE7 borks horribly if we use documentElement.
31165         // webkit also does not like documentElement - it creates a body element...
31166         el = Roo.get( document.body || document.documentElement ).createChild();
31167         //config.autoCreate = true;
31168     }
31169     
31170     
31171     config.autoTabs = false;
31172     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31173     this.body.setStyle({overflow:"hidden", position:"relative"});
31174     this.layout = new Roo.BorderLayout(this.body.dom, config);
31175     this.layout.monitorWindowResize = false;
31176     this.el.addClass("x-dlg-auto-layout");
31177     // fix case when center region overwrites center function
31178     this.center = Roo.BasicDialog.prototype.center;
31179     this.on("show", this.layout.layout, this.layout, true);
31180     if (config.items) {
31181         var xitems = config.items;
31182         delete config.items;
31183         Roo.each(xitems, this.addxtype, this);
31184     }
31185     
31186     
31187 };
31188 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31189     /**
31190      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31191      * @deprecated
31192      */
31193     endUpdate : function(){
31194         this.layout.endUpdate();
31195     },
31196
31197     /**
31198      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31199      *  @deprecated
31200      */
31201     beginUpdate : function(){
31202         this.layout.beginUpdate();
31203     },
31204
31205     /**
31206      * Get the BorderLayout for this dialog
31207      * @return {Roo.BorderLayout}
31208      */
31209     getLayout : function(){
31210         return this.layout;
31211     },
31212
31213     showEl : function(){
31214         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31215         if(Roo.isIE7){
31216             this.layout.layout();
31217         }
31218     },
31219
31220     // private
31221     // Use the syncHeightBeforeShow config option to control this automatically
31222     syncBodyHeight : function(){
31223         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31224         if(this.layout){this.layout.layout();}
31225     },
31226     
31227       /**
31228      * Add an xtype element (actually adds to the layout.)
31229      * @return {Object} xdata xtype object data.
31230      */
31231     
31232     addxtype : function(c) {
31233         return this.layout.addxtype(c);
31234     }
31235 });/*
31236  * Based on:
31237  * Ext JS Library 1.1.1
31238  * Copyright(c) 2006-2007, Ext JS, LLC.
31239  *
31240  * Originally Released Under LGPL - original licence link has changed is not relivant.
31241  *
31242  * Fork - LGPL
31243  * <script type="text/javascript">
31244  */
31245  
31246 /**
31247  * @class Roo.MessageBox
31248  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31249  * Example usage:
31250  *<pre><code>
31251 // Basic alert:
31252 Roo.Msg.alert('Status', 'Changes saved successfully.');
31253
31254 // Prompt for user data:
31255 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31256     if (btn == 'ok'){
31257         // process text value...
31258     }
31259 });
31260
31261 // Show a dialog using config options:
31262 Roo.Msg.show({
31263    title:'Save Changes?',
31264    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31265    buttons: Roo.Msg.YESNOCANCEL,
31266    fn: processResult,
31267    animEl: 'elId'
31268 });
31269 </code></pre>
31270  * @singleton
31271  */
31272 Roo.MessageBox = function(){
31273     var dlg, opt, mask, waitTimer;
31274     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31275     var buttons, activeTextEl, bwidth;
31276
31277     // private
31278     var handleButton = function(button){
31279         dlg.hide();
31280         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31281     };
31282
31283     // private
31284     var handleHide = function(){
31285         if(opt && opt.cls){
31286             dlg.el.removeClass(opt.cls);
31287         }
31288         if(waitTimer){
31289             Roo.TaskMgr.stop(waitTimer);
31290             waitTimer = null;
31291         }
31292     };
31293
31294     // private
31295     var updateButtons = function(b){
31296         var width = 0;
31297         if(!b){
31298             buttons["ok"].hide();
31299             buttons["cancel"].hide();
31300             buttons["yes"].hide();
31301             buttons["no"].hide();
31302             dlg.footer.dom.style.display = 'none';
31303             return width;
31304         }
31305         dlg.footer.dom.style.display = '';
31306         for(var k in buttons){
31307             if(typeof buttons[k] != "function"){
31308                 if(b[k]){
31309                     buttons[k].show();
31310                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31311                     width += buttons[k].el.getWidth()+15;
31312                 }else{
31313                     buttons[k].hide();
31314                 }
31315             }
31316         }
31317         return width;
31318     };
31319
31320     // private
31321     var handleEsc = function(d, k, e){
31322         if(opt && opt.closable !== false){
31323             dlg.hide();
31324         }
31325         if(e){
31326             e.stopEvent();
31327         }
31328     };
31329
31330     return {
31331         /**
31332          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31333          * @return {Roo.BasicDialog} The BasicDialog element
31334          */
31335         getDialog : function(){
31336            if(!dlg){
31337                 dlg = new Roo.BasicDialog("x-msg-box", {
31338                     autoCreate : true,
31339                     shadow: true,
31340                     draggable: true,
31341                     resizable:false,
31342                     constraintoviewport:false,
31343                     fixedcenter:true,
31344                     collapsible : false,
31345                     shim:true,
31346                     modal: true,
31347                     width:400, height:100,
31348                     buttonAlign:"center",
31349                     closeClick : function(){
31350                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31351                             handleButton("no");
31352                         }else{
31353                             handleButton("cancel");
31354                         }
31355                     }
31356                 });
31357                 dlg.on("hide", handleHide);
31358                 mask = dlg.mask;
31359                 dlg.addKeyListener(27, handleEsc);
31360                 buttons = {};
31361                 var bt = this.buttonText;
31362                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31363                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31364                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31365                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31366                 bodyEl = dlg.body.createChild({
31367
31368                     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>'
31369                 });
31370                 msgEl = bodyEl.dom.firstChild;
31371                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31372                 textboxEl.enableDisplayMode();
31373                 textboxEl.addKeyListener([10,13], function(){
31374                     if(dlg.isVisible() && opt && opt.buttons){
31375                         if(opt.buttons.ok){
31376                             handleButton("ok");
31377                         }else if(opt.buttons.yes){
31378                             handleButton("yes");
31379                         }
31380                     }
31381                 });
31382                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31383                 textareaEl.enableDisplayMode();
31384                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31385                 progressEl.enableDisplayMode();
31386                 var pf = progressEl.dom.firstChild;
31387                 if (pf) {
31388                     pp = Roo.get(pf.firstChild);
31389                     pp.setHeight(pf.offsetHeight);
31390                 }
31391                 
31392             }
31393             return dlg;
31394         },
31395
31396         /**
31397          * Updates the message box body text
31398          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31399          * the XHTML-compliant non-breaking space character '&amp;#160;')
31400          * @return {Roo.MessageBox} This message box
31401          */
31402         updateText : function(text){
31403             if(!dlg.isVisible() && !opt.width){
31404                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31405             }
31406             msgEl.innerHTML = text || '&#160;';
31407       
31408             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31409             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31410             var w = Math.max(
31411                     Math.min(opt.width || cw , this.maxWidth), 
31412                     Math.max(opt.minWidth || this.minWidth, bwidth)
31413             );
31414             if(opt.prompt){
31415                 activeTextEl.setWidth(w);
31416             }
31417             if(dlg.isVisible()){
31418                 dlg.fixedcenter = false;
31419             }
31420             // to big, make it scroll. = But as usual stupid IE does not support
31421             // !important..
31422             
31423             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31424                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31425                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31426             } else {
31427                 bodyEl.dom.style.height = '';
31428                 bodyEl.dom.style.overflowY = '';
31429             }
31430             if (cw > w) {
31431                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31432             } else {
31433                 bodyEl.dom.style.overflowX = '';
31434             }
31435             
31436             dlg.setContentSize(w, bodyEl.getHeight());
31437             if(dlg.isVisible()){
31438                 dlg.fixedcenter = true;
31439             }
31440             return this;
31441         },
31442
31443         /**
31444          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31445          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31446          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31447          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31448          * @return {Roo.MessageBox} This message box
31449          */
31450         updateProgress : function(value, text){
31451             if(text){
31452                 this.updateText(text);
31453             }
31454             if (pp) { // weird bug on my firefox - for some reason this is not defined
31455                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31456             }
31457             return this;
31458         },        
31459
31460         /**
31461          * Returns true if the message box is currently displayed
31462          * @return {Boolean} True if the message box is visible, else false
31463          */
31464         isVisible : function(){
31465             return dlg && dlg.isVisible();  
31466         },
31467
31468         /**
31469          * Hides the message box if it is displayed
31470          */
31471         hide : function(){
31472             if(this.isVisible()){
31473                 dlg.hide();
31474             }  
31475         },
31476
31477         /**
31478          * Displays a new message box, or reinitializes an existing message box, based on the config options
31479          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31480          * The following config object properties are supported:
31481          * <pre>
31482 Property    Type             Description
31483 ----------  ---------------  ------------------------------------------------------------------------------------
31484 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31485                                    closes (defaults to undefined)
31486 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31487                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31488 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31489                                    progress and wait dialogs will ignore this property and always hide the
31490                                    close button as they can only be closed programmatically.
31491 cls               String           A custom CSS class to apply to the message box element
31492 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31493                                    displayed (defaults to 75)
31494 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31495                                    function will be btn (the name of the button that was clicked, if applicable,
31496                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31497                                    Progress and wait dialogs will ignore this option since they do not respond to
31498                                    user actions and can only be closed programmatically, so any required function
31499                                    should be called by the same code after it closes the dialog.
31500 icon              String           A CSS class that provides a background image to be used as an icon for
31501                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31502 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31503 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31504 modal             Boolean          False to allow user interaction with the page while the message box is
31505                                    displayed (defaults to true)
31506 msg               String           A string that will replace the existing message box body text (defaults
31507                                    to the XHTML-compliant non-breaking space character '&#160;')
31508 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31509 progress          Boolean          True to display a progress bar (defaults to false)
31510 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31511 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31512 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31513 title             String           The title text
31514 value             String           The string value to set into the active textbox element if displayed
31515 wait              Boolean          True to display a progress bar (defaults to false)
31516 width             Number           The width of the dialog in pixels
31517 </pre>
31518          *
31519          * Example usage:
31520          * <pre><code>
31521 Roo.Msg.show({
31522    title: 'Address',
31523    msg: 'Please enter your address:',
31524    width: 300,
31525    buttons: Roo.MessageBox.OKCANCEL,
31526    multiline: true,
31527    fn: saveAddress,
31528    animEl: 'addAddressBtn'
31529 });
31530 </code></pre>
31531          * @param {Object} config Configuration options
31532          * @return {Roo.MessageBox} This message box
31533          */
31534         show : function(options)
31535         {
31536             
31537             // this causes nightmares if you show one dialog after another
31538             // especially on callbacks..
31539              
31540             if(this.isVisible()){
31541                 
31542                 this.hide();
31543                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31544                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31545                 Roo.log("New Dialog Message:" +  options.msg )
31546                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31547                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31548                 
31549             }
31550             var d = this.getDialog();
31551             opt = options;
31552             d.setTitle(opt.title || "&#160;");
31553             d.close.setDisplayed(opt.closable !== false);
31554             activeTextEl = textboxEl;
31555             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31556             if(opt.prompt){
31557                 if(opt.multiline){
31558                     textboxEl.hide();
31559                     textareaEl.show();
31560                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31561                         opt.multiline : this.defaultTextHeight);
31562                     activeTextEl = textareaEl;
31563                 }else{
31564                     textboxEl.show();
31565                     textareaEl.hide();
31566                 }
31567             }else{
31568                 textboxEl.hide();
31569                 textareaEl.hide();
31570             }
31571             progressEl.setDisplayed(opt.progress === true);
31572             this.updateProgress(0);
31573             activeTextEl.dom.value = opt.value || "";
31574             if(opt.prompt){
31575                 dlg.setDefaultButton(activeTextEl);
31576             }else{
31577                 var bs = opt.buttons;
31578                 var db = null;
31579                 if(bs && bs.ok){
31580                     db = buttons["ok"];
31581                 }else if(bs && bs.yes){
31582                     db = buttons["yes"];
31583                 }
31584                 dlg.setDefaultButton(db);
31585             }
31586             bwidth = updateButtons(opt.buttons);
31587             this.updateText(opt.msg);
31588             if(opt.cls){
31589                 d.el.addClass(opt.cls);
31590             }
31591             d.proxyDrag = opt.proxyDrag === true;
31592             d.modal = opt.modal !== false;
31593             d.mask = opt.modal !== false ? mask : false;
31594             if(!d.isVisible()){
31595                 // force it to the end of the z-index stack so it gets a cursor in FF
31596                 document.body.appendChild(dlg.el.dom);
31597                 d.animateTarget = null;
31598                 d.show(options.animEl);
31599             }
31600             return this;
31601         },
31602
31603         /**
31604          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31605          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31606          * and closing the message box when the process is complete.
31607          * @param {String} title The title bar text
31608          * @param {String} msg The message box body text
31609          * @return {Roo.MessageBox} This message box
31610          */
31611         progress : function(title, msg){
31612             this.show({
31613                 title : title,
31614                 msg : msg,
31615                 buttons: false,
31616                 progress:true,
31617                 closable:false,
31618                 minWidth: this.minProgressWidth,
31619                 modal : true
31620             });
31621             return this;
31622         },
31623
31624         /**
31625          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31626          * If a callback function is passed it will be called after the user clicks the button, and the
31627          * id of the button that was clicked will be passed as the only parameter to the callback
31628          * (could also be the top-right close button).
31629          * @param {String} title The title bar text
31630          * @param {String} msg The message box body text
31631          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31632          * @param {Object} scope (optional) The scope of the callback function
31633          * @return {Roo.MessageBox} This message box
31634          */
31635         alert : function(title, msg, fn, scope){
31636             this.show({
31637                 title : title,
31638                 msg : msg,
31639                 buttons: this.OK,
31640                 fn: fn,
31641                 scope : scope,
31642                 modal : true
31643             });
31644             return this;
31645         },
31646
31647         /**
31648          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31649          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31650          * You are responsible for closing the message box when the process is complete.
31651          * @param {String} msg The message box body text
31652          * @param {String} title (optional) The title bar text
31653          * @return {Roo.MessageBox} This message box
31654          */
31655         wait : function(msg, title){
31656             this.show({
31657                 title : title,
31658                 msg : msg,
31659                 buttons: false,
31660                 closable:false,
31661                 progress:true,
31662                 modal:true,
31663                 width:300,
31664                 wait:true
31665             });
31666             waitTimer = Roo.TaskMgr.start({
31667                 run: function(i){
31668                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31669                 },
31670                 interval: 1000
31671             });
31672             return this;
31673         },
31674
31675         /**
31676          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31677          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31678          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31679          * @param {String} title The title bar text
31680          * @param {String} msg The message box body text
31681          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31682          * @param {Object} scope (optional) The scope of the callback function
31683          * @return {Roo.MessageBox} This message box
31684          */
31685         confirm : function(title, msg, fn, scope){
31686             this.show({
31687                 title : title,
31688                 msg : msg,
31689                 buttons: this.YESNO,
31690                 fn: fn,
31691                 scope : scope,
31692                 modal : true
31693             });
31694             return this;
31695         },
31696
31697         /**
31698          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31699          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31700          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31701          * (could also be the top-right close button) and the text that was entered will be passed as the two
31702          * parameters to the callback.
31703          * @param {String} title The title bar text
31704          * @param {String} msg The message box body text
31705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31706          * @param {Object} scope (optional) The scope of the callback function
31707          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31708          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31709          * @return {Roo.MessageBox} This message box
31710          */
31711         prompt : function(title, msg, fn, scope, multiline){
31712             this.show({
31713                 title : title,
31714                 msg : msg,
31715                 buttons: this.OKCANCEL,
31716                 fn: fn,
31717                 minWidth:250,
31718                 scope : scope,
31719                 prompt:true,
31720                 multiline: multiline,
31721                 modal : true
31722             });
31723             return this;
31724         },
31725
31726         /**
31727          * Button config that displays a single OK button
31728          * @type Object
31729          */
31730         OK : {ok:true},
31731         /**
31732          * Button config that displays Yes and No buttons
31733          * @type Object
31734          */
31735         YESNO : {yes:true, no:true},
31736         /**
31737          * Button config that displays OK and Cancel buttons
31738          * @type Object
31739          */
31740         OKCANCEL : {ok:true, cancel:true},
31741         /**
31742          * Button config that displays Yes, No and Cancel buttons
31743          * @type Object
31744          */
31745         YESNOCANCEL : {yes:true, no:true, cancel:true},
31746
31747         /**
31748          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31749          * @type Number
31750          */
31751         defaultTextHeight : 75,
31752         /**
31753          * The maximum width in pixels of the message box (defaults to 600)
31754          * @type Number
31755          */
31756         maxWidth : 600,
31757         /**
31758          * The minimum width in pixels of the message box (defaults to 100)
31759          * @type Number
31760          */
31761         minWidth : 100,
31762         /**
31763          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31764          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31765          * @type Number
31766          */
31767         minProgressWidth : 250,
31768         /**
31769          * An object containing the default button text strings that can be overriden for localized language support.
31770          * Supported properties are: ok, cancel, yes and no.
31771          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31772          * @type Object
31773          */
31774         buttonText : {
31775             ok : "OK",
31776             cancel : "Cancel",
31777             yes : "Yes",
31778             no : "No"
31779         }
31780     };
31781 }();
31782
31783 /**
31784  * Shorthand for {@link Roo.MessageBox}
31785  */
31786 Roo.Msg = Roo.MessageBox;/*
31787  * Based on:
31788  * Ext JS Library 1.1.1
31789  * Copyright(c) 2006-2007, Ext JS, LLC.
31790  *
31791  * Originally Released Under LGPL - original licence link has changed is not relivant.
31792  *
31793  * Fork - LGPL
31794  * <script type="text/javascript">
31795  */
31796 /**
31797  * @class Roo.QuickTips
31798  * Provides attractive and customizable tooltips for any element.
31799  * @singleton
31800  */
31801 Roo.QuickTips = function(){
31802     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31803     var ce, bd, xy, dd;
31804     var visible = false, disabled = true, inited = false;
31805     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31806     
31807     var onOver = function(e){
31808         if(disabled){
31809             return;
31810         }
31811         var t = e.getTarget();
31812         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31813             return;
31814         }
31815         if(ce && t == ce.el){
31816             clearTimeout(hideProc);
31817             return;
31818         }
31819         if(t && tagEls[t.id]){
31820             tagEls[t.id].el = t;
31821             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31822             return;
31823         }
31824         var ttp, et = Roo.fly(t);
31825         var ns = cfg.namespace;
31826         if(tm.interceptTitles && t.title){
31827             ttp = t.title;
31828             t.qtip = ttp;
31829             t.removeAttribute("title");
31830             e.preventDefault();
31831         }else{
31832             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31833         }
31834         if(ttp){
31835             showProc = show.defer(tm.showDelay, tm, [{
31836                 el: t, 
31837                 text: ttp, 
31838                 width: et.getAttributeNS(ns, cfg.width),
31839                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31840                 title: et.getAttributeNS(ns, cfg.title),
31841                     cls: et.getAttributeNS(ns, cfg.cls)
31842             }]);
31843         }
31844     };
31845     
31846     var onOut = function(e){
31847         clearTimeout(showProc);
31848         var t = e.getTarget();
31849         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31850             hideProc = setTimeout(hide, tm.hideDelay);
31851         }
31852     };
31853     
31854     var onMove = function(e){
31855         if(disabled){
31856             return;
31857         }
31858         xy = e.getXY();
31859         xy[1] += 18;
31860         if(tm.trackMouse && ce){
31861             el.setXY(xy);
31862         }
31863     };
31864     
31865     var onDown = function(e){
31866         clearTimeout(showProc);
31867         clearTimeout(hideProc);
31868         if(!e.within(el)){
31869             if(tm.hideOnClick){
31870                 hide();
31871                 tm.disable();
31872                 tm.enable.defer(100, tm);
31873             }
31874         }
31875     };
31876     
31877     var getPad = function(){
31878         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31879     };
31880
31881     var show = function(o){
31882         if(disabled){
31883             return;
31884         }
31885         clearTimeout(dismissProc);
31886         ce = o;
31887         if(removeCls){ // in case manually hidden
31888             el.removeClass(removeCls);
31889             removeCls = null;
31890         }
31891         if(ce.cls){
31892             el.addClass(ce.cls);
31893             removeCls = ce.cls;
31894         }
31895         if(ce.title){
31896             tipTitle.update(ce.title);
31897             tipTitle.show();
31898         }else{
31899             tipTitle.update('');
31900             tipTitle.hide();
31901         }
31902         el.dom.style.width  = tm.maxWidth+'px';
31903         //tipBody.dom.style.width = '';
31904         tipBodyText.update(o.text);
31905         var p = getPad(), w = ce.width;
31906         if(!w){
31907             var td = tipBodyText.dom;
31908             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31909             if(aw > tm.maxWidth){
31910                 w = tm.maxWidth;
31911             }else if(aw < tm.minWidth){
31912                 w = tm.minWidth;
31913             }else{
31914                 w = aw;
31915             }
31916         }
31917         //tipBody.setWidth(w);
31918         el.setWidth(parseInt(w, 10) + p);
31919         if(ce.autoHide === false){
31920             close.setDisplayed(true);
31921             if(dd){
31922                 dd.unlock();
31923             }
31924         }else{
31925             close.setDisplayed(false);
31926             if(dd){
31927                 dd.lock();
31928             }
31929         }
31930         if(xy){
31931             el.avoidY = xy[1]-18;
31932             el.setXY(xy);
31933         }
31934         if(tm.animate){
31935             el.setOpacity(.1);
31936             el.setStyle("visibility", "visible");
31937             el.fadeIn({callback: afterShow});
31938         }else{
31939             afterShow();
31940         }
31941     };
31942     
31943     var afterShow = function(){
31944         if(ce){
31945             el.show();
31946             esc.enable();
31947             if(tm.autoDismiss && ce.autoHide !== false){
31948                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31949             }
31950         }
31951     };
31952     
31953     var hide = function(noanim){
31954         clearTimeout(dismissProc);
31955         clearTimeout(hideProc);
31956         ce = null;
31957         if(el.isVisible()){
31958             esc.disable();
31959             if(noanim !== true && tm.animate){
31960                 el.fadeOut({callback: afterHide});
31961             }else{
31962                 afterHide();
31963             } 
31964         }
31965     };
31966     
31967     var afterHide = function(){
31968         el.hide();
31969         if(removeCls){
31970             el.removeClass(removeCls);
31971             removeCls = null;
31972         }
31973     };
31974     
31975     return {
31976         /**
31977         * @cfg {Number} minWidth
31978         * The minimum width of the quick tip (defaults to 40)
31979         */
31980        minWidth : 40,
31981         /**
31982         * @cfg {Number} maxWidth
31983         * The maximum width of the quick tip (defaults to 300)
31984         */
31985        maxWidth : 300,
31986         /**
31987         * @cfg {Boolean} interceptTitles
31988         * True to automatically use the element's DOM title value if available (defaults to false)
31989         */
31990        interceptTitles : false,
31991         /**
31992         * @cfg {Boolean} trackMouse
31993         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31994         */
31995        trackMouse : false,
31996         /**
31997         * @cfg {Boolean} hideOnClick
31998         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31999         */
32000        hideOnClick : true,
32001         /**
32002         * @cfg {Number} showDelay
32003         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32004         */
32005        showDelay : 500,
32006         /**
32007         * @cfg {Number} hideDelay
32008         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32009         */
32010        hideDelay : 200,
32011         /**
32012         * @cfg {Boolean} autoHide
32013         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32014         * Used in conjunction with hideDelay.
32015         */
32016        autoHide : true,
32017         /**
32018         * @cfg {Boolean}
32019         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32020         * (defaults to true).  Used in conjunction with autoDismissDelay.
32021         */
32022        autoDismiss : true,
32023         /**
32024         * @cfg {Number}
32025         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32026         */
32027        autoDismissDelay : 5000,
32028        /**
32029         * @cfg {Boolean} animate
32030         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32031         */
32032        animate : false,
32033
32034        /**
32035         * @cfg {String} title
32036         * Title text to display (defaults to '').  This can be any valid HTML markup.
32037         */
32038         title: '',
32039        /**
32040         * @cfg {String} text
32041         * Body text to display (defaults to '').  This can be any valid HTML markup.
32042         */
32043         text : '',
32044        /**
32045         * @cfg {String} cls
32046         * A CSS class to apply to the base quick tip element (defaults to '').
32047         */
32048         cls : '',
32049        /**
32050         * @cfg {Number} width
32051         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32052         * minWidth or maxWidth.
32053         */
32054         width : null,
32055
32056     /**
32057      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32058      * or display QuickTips in a page.
32059      */
32060        init : function(){
32061           tm = Roo.QuickTips;
32062           cfg = tm.tagConfig;
32063           if(!inited){
32064               if(!Roo.isReady){ // allow calling of init() before onReady
32065                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32066                   return;
32067               }
32068               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32069               el.fxDefaults = {stopFx: true};
32070               // maximum custom styling
32071               //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>');
32072               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>');              
32073               tipTitle = el.child('h3');
32074               tipTitle.enableDisplayMode("block");
32075               tipBody = el.child('div.x-tip-bd');
32076               tipBodyText = el.child('div.x-tip-bd-inner');
32077               //bdLeft = el.child('div.x-tip-bd-left');
32078               //bdRight = el.child('div.x-tip-bd-right');
32079               close = el.child('div.x-tip-close');
32080               close.enableDisplayMode("block");
32081               close.on("click", hide);
32082               var d = Roo.get(document);
32083               d.on("mousedown", onDown);
32084               d.on("mouseover", onOver);
32085               d.on("mouseout", onOut);
32086               d.on("mousemove", onMove);
32087               esc = d.addKeyListener(27, hide);
32088               esc.disable();
32089               if(Roo.dd.DD){
32090                   dd = el.initDD("default", null, {
32091                       onDrag : function(){
32092                           el.sync();  
32093                       }
32094                   });
32095                   dd.setHandleElId(tipTitle.id);
32096                   dd.lock();
32097               }
32098               inited = true;
32099           }
32100           this.enable(); 
32101        },
32102
32103     /**
32104      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32105      * are supported:
32106      * <pre>
32107 Property    Type                   Description
32108 ----------  ---------------------  ------------------------------------------------------------------------
32109 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32110      * </ul>
32111      * @param {Object} config The config object
32112      */
32113        register : function(config){
32114            var cs = config instanceof Array ? config : arguments;
32115            for(var i = 0, len = cs.length; i < len; i++) {
32116                var c = cs[i];
32117                var target = c.target;
32118                if(target){
32119                    if(target instanceof Array){
32120                        for(var j = 0, jlen = target.length; j < jlen; j++){
32121                            tagEls[target[j]] = c;
32122                        }
32123                    }else{
32124                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32125                    }
32126                }
32127            }
32128        },
32129
32130     /**
32131      * Removes this quick tip from its element and destroys it.
32132      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32133      */
32134        unregister : function(el){
32135            delete tagEls[Roo.id(el)];
32136        },
32137
32138     /**
32139      * Enable this quick tip.
32140      */
32141        enable : function(){
32142            if(inited && disabled){
32143                locks.pop();
32144                if(locks.length < 1){
32145                    disabled = false;
32146                }
32147            }
32148        },
32149
32150     /**
32151      * Disable this quick tip.
32152      */
32153        disable : function(){
32154           disabled = true;
32155           clearTimeout(showProc);
32156           clearTimeout(hideProc);
32157           clearTimeout(dismissProc);
32158           if(ce){
32159               hide(true);
32160           }
32161           locks.push(1);
32162        },
32163
32164     /**
32165      * Returns true if the quick tip is enabled, else false.
32166      */
32167        isEnabled : function(){
32168             return !disabled;
32169        },
32170
32171         // private
32172        tagConfig : {
32173            namespace : "ext",
32174            attribute : "qtip",
32175            width : "width",
32176            target : "target",
32177            title : "qtitle",
32178            hide : "hide",
32179            cls : "qclass"
32180        }
32181    };
32182 }();
32183
32184 // backwards compat
32185 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32186  * Based on:
32187  * Ext JS Library 1.1.1
32188  * Copyright(c) 2006-2007, Ext JS, LLC.
32189  *
32190  * Originally Released Under LGPL - original licence link has changed is not relivant.
32191  *
32192  * Fork - LGPL
32193  * <script type="text/javascript">
32194  */
32195  
32196
32197 /**
32198  * @class Roo.tree.TreePanel
32199  * @extends Roo.data.Tree
32200
32201  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32202  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32203  * @cfg {Boolean} enableDD true to enable drag and drop
32204  * @cfg {Boolean} enableDrag true to enable just drag
32205  * @cfg {Boolean} enableDrop true to enable just drop
32206  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32207  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32208  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32209  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32210  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32211  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32212  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32213  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32214  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32215  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32216  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32217  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32218  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32219  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32220  * @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>
32221  * @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>
32222  * 
32223  * @constructor
32224  * @param {String/HTMLElement/Element} el The container element
32225  * @param {Object} config
32226  */
32227 Roo.tree.TreePanel = function(el, config){
32228     var root = false;
32229     var loader = false;
32230     if (config.root) {
32231         root = config.root;
32232         delete config.root;
32233     }
32234     if (config.loader) {
32235         loader = config.loader;
32236         delete config.loader;
32237     }
32238     
32239     Roo.apply(this, config);
32240     Roo.tree.TreePanel.superclass.constructor.call(this);
32241     this.el = Roo.get(el);
32242     this.el.addClass('x-tree');
32243     //console.log(root);
32244     if (root) {
32245         this.setRootNode( Roo.factory(root, Roo.tree));
32246     }
32247     if (loader) {
32248         this.loader = Roo.factory(loader, Roo.tree);
32249     }
32250    /**
32251     * Read-only. The id of the container element becomes this TreePanel's id.
32252     */
32253     this.id = this.el.id;
32254     this.addEvents({
32255         /**
32256         * @event beforeload
32257         * Fires before a node is loaded, return false to cancel
32258         * @param {Node} node The node being loaded
32259         */
32260         "beforeload" : true,
32261         /**
32262         * @event load
32263         * Fires when a node is loaded
32264         * @param {Node} node The node that was loaded
32265         */
32266         "load" : true,
32267         /**
32268         * @event textchange
32269         * Fires when the text for a node is changed
32270         * @param {Node} node The node
32271         * @param {String} text The new text
32272         * @param {String} oldText The old text
32273         */
32274         "textchange" : true,
32275         /**
32276         * @event beforeexpand
32277         * Fires before a node is expanded, return false to cancel.
32278         * @param {Node} node The node
32279         * @param {Boolean} deep
32280         * @param {Boolean} anim
32281         */
32282         "beforeexpand" : true,
32283         /**
32284         * @event beforecollapse
32285         * Fires before a node is collapsed, return false to cancel.
32286         * @param {Node} node The node
32287         * @param {Boolean} deep
32288         * @param {Boolean} anim
32289         */
32290         "beforecollapse" : true,
32291         /**
32292         * @event expand
32293         * Fires when a node is expanded
32294         * @param {Node} node The node
32295         */
32296         "expand" : true,
32297         /**
32298         * @event disabledchange
32299         * Fires when the disabled status of a node changes
32300         * @param {Node} node The node
32301         * @param {Boolean} disabled
32302         */
32303         "disabledchange" : true,
32304         /**
32305         * @event collapse
32306         * Fires when a node is collapsed
32307         * @param {Node} node The node
32308         */
32309         "collapse" : true,
32310         /**
32311         * @event beforeclick
32312         * Fires before click processing on a node. Return false to cancel the default action.
32313         * @param {Node} node The node
32314         * @param {Roo.EventObject} e The event object
32315         */
32316         "beforeclick":true,
32317         /**
32318         * @event checkchange
32319         * Fires when a node with a checkbox's checked property changes
32320         * @param {Node} this This node
32321         * @param {Boolean} checked
32322         */
32323         "checkchange":true,
32324         /**
32325         * @event click
32326         * Fires when a node is clicked
32327         * @param {Node} node The node
32328         * @param {Roo.EventObject} e The event object
32329         */
32330         "click":true,
32331         /**
32332         * @event dblclick
32333         * Fires when a node is double clicked
32334         * @param {Node} node The node
32335         * @param {Roo.EventObject} e The event object
32336         */
32337         "dblclick":true,
32338         /**
32339         * @event contextmenu
32340         * Fires when a node is right clicked
32341         * @param {Node} node The node
32342         * @param {Roo.EventObject} e The event object
32343         */
32344         "contextmenu":true,
32345         /**
32346         * @event beforechildrenrendered
32347         * Fires right before the child nodes for a node are rendered
32348         * @param {Node} node The node
32349         */
32350         "beforechildrenrendered":true,
32351         /**
32352         * @event startdrag
32353         * Fires when a node starts being dragged
32354         * @param {Roo.tree.TreePanel} this
32355         * @param {Roo.tree.TreeNode} node
32356         * @param {event} e The raw browser event
32357         */ 
32358        "startdrag" : true,
32359        /**
32360         * @event enddrag
32361         * Fires when a drag operation is complete
32362         * @param {Roo.tree.TreePanel} this
32363         * @param {Roo.tree.TreeNode} node
32364         * @param {event} e The raw browser event
32365         */
32366        "enddrag" : true,
32367        /**
32368         * @event dragdrop
32369         * Fires when a dragged node is dropped on a valid DD target
32370         * @param {Roo.tree.TreePanel} this
32371         * @param {Roo.tree.TreeNode} node
32372         * @param {DD} dd The dd it was dropped on
32373         * @param {event} e The raw browser event
32374         */
32375        "dragdrop" : true,
32376        /**
32377         * @event beforenodedrop
32378         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32379         * passed to handlers has the following properties:<br />
32380         * <ul style="padding:5px;padding-left:16px;">
32381         * <li>tree - The TreePanel</li>
32382         * <li>target - The node being targeted for the drop</li>
32383         * <li>data - The drag data from the drag source</li>
32384         * <li>point - The point of the drop - append, above or below</li>
32385         * <li>source - The drag source</li>
32386         * <li>rawEvent - Raw mouse event</li>
32387         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32388         * to be inserted by setting them on this object.</li>
32389         * <li>cancel - Set this to true to cancel the drop.</li>
32390         * </ul>
32391         * @param {Object} dropEvent
32392         */
32393        "beforenodedrop" : true,
32394        /**
32395         * @event nodedrop
32396         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32397         * passed to handlers has the following properties:<br />
32398         * <ul style="padding:5px;padding-left:16px;">
32399         * <li>tree - The TreePanel</li>
32400         * <li>target - The node being targeted for the drop</li>
32401         * <li>data - The drag data from the drag source</li>
32402         * <li>point - The point of the drop - append, above or below</li>
32403         * <li>source - The drag source</li>
32404         * <li>rawEvent - Raw mouse event</li>
32405         * <li>dropNode - Dropped node(s).</li>
32406         * </ul>
32407         * @param {Object} dropEvent
32408         */
32409        "nodedrop" : true,
32410         /**
32411         * @event nodedragover
32412         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32413         * passed to handlers has the following properties:<br />
32414         * <ul style="padding:5px;padding-left:16px;">
32415         * <li>tree - The TreePanel</li>
32416         * <li>target - The node being targeted for the drop</li>
32417         * <li>data - The drag data from the drag source</li>
32418         * <li>point - The point of the drop - append, above or below</li>
32419         * <li>source - The drag source</li>
32420         * <li>rawEvent - Raw mouse event</li>
32421         * <li>dropNode - Drop node(s) provided by the source.</li>
32422         * <li>cancel - Set this to true to signal drop not allowed.</li>
32423         * </ul>
32424         * @param {Object} dragOverEvent
32425         */
32426        "nodedragover" : true
32427         
32428     });
32429     if(this.singleExpand){
32430        this.on("beforeexpand", this.restrictExpand, this);
32431     }
32432     if (this.editor) {
32433         this.editor.tree = this;
32434         this.editor = Roo.factory(this.editor, Roo.tree);
32435     }
32436     
32437     if (this.selModel) {
32438         this.selModel = Roo.factory(this.selModel, Roo.tree);
32439     }
32440    
32441 };
32442 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32443     rootVisible : true,
32444     animate: Roo.enableFx,
32445     lines : true,
32446     enableDD : false,
32447     hlDrop : Roo.enableFx,
32448   
32449     renderer: false,
32450     
32451     rendererTip: false,
32452     // private
32453     restrictExpand : function(node){
32454         var p = node.parentNode;
32455         if(p){
32456             if(p.expandedChild && p.expandedChild.parentNode == p){
32457                 p.expandedChild.collapse();
32458             }
32459             p.expandedChild = node;
32460         }
32461     },
32462
32463     // private override
32464     setRootNode : function(node){
32465         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32466         if(!this.rootVisible){
32467             node.ui = new Roo.tree.RootTreeNodeUI(node);
32468         }
32469         return node;
32470     },
32471
32472     /**
32473      * Returns the container element for this TreePanel
32474      */
32475     getEl : function(){
32476         return this.el;
32477     },
32478
32479     /**
32480      * Returns the default TreeLoader for this TreePanel
32481      */
32482     getLoader : function(){
32483         return this.loader;
32484     },
32485
32486     /**
32487      * Expand all nodes
32488      */
32489     expandAll : function(){
32490         this.root.expand(true);
32491     },
32492
32493     /**
32494      * Collapse all nodes
32495      */
32496     collapseAll : function(){
32497         this.root.collapse(true);
32498     },
32499
32500     /**
32501      * Returns the selection model used by this TreePanel
32502      */
32503     getSelectionModel : function(){
32504         if(!this.selModel){
32505             this.selModel = new Roo.tree.DefaultSelectionModel();
32506         }
32507         return this.selModel;
32508     },
32509
32510     /**
32511      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32512      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32513      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32514      * @return {Array}
32515      */
32516     getChecked : function(a, startNode){
32517         startNode = startNode || this.root;
32518         var r = [];
32519         var f = function(){
32520             if(this.attributes.checked){
32521                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32522             }
32523         }
32524         startNode.cascade(f);
32525         return r;
32526     },
32527
32528     /**
32529      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32530      * @param {String} path
32531      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32532      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32533      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32534      */
32535     expandPath : function(path, attr, callback){
32536         attr = attr || "id";
32537         var keys = path.split(this.pathSeparator);
32538         var curNode = this.root;
32539         if(curNode.attributes[attr] != keys[1]){ // invalid root
32540             if(callback){
32541                 callback(false, null);
32542             }
32543             return;
32544         }
32545         var index = 1;
32546         var f = function(){
32547             if(++index == keys.length){
32548                 if(callback){
32549                     callback(true, curNode);
32550                 }
32551                 return;
32552             }
32553             var c = curNode.findChild(attr, keys[index]);
32554             if(!c){
32555                 if(callback){
32556                     callback(false, curNode);
32557                 }
32558                 return;
32559             }
32560             curNode = c;
32561             c.expand(false, false, f);
32562         };
32563         curNode.expand(false, false, f);
32564     },
32565
32566     /**
32567      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32568      * @param {String} path
32569      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32570      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32571      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32572      */
32573     selectPath : function(path, attr, callback){
32574         attr = attr || "id";
32575         var keys = path.split(this.pathSeparator);
32576         var v = keys.pop();
32577         if(keys.length > 0){
32578             var f = function(success, node){
32579                 if(success && node){
32580                     var n = node.findChild(attr, v);
32581                     if(n){
32582                         n.select();
32583                         if(callback){
32584                             callback(true, n);
32585                         }
32586                     }else if(callback){
32587                         callback(false, n);
32588                     }
32589                 }else{
32590                     if(callback){
32591                         callback(false, n);
32592                     }
32593                 }
32594             };
32595             this.expandPath(keys.join(this.pathSeparator), attr, f);
32596         }else{
32597             this.root.select();
32598             if(callback){
32599                 callback(true, this.root);
32600             }
32601         }
32602     },
32603
32604     getTreeEl : function(){
32605         return this.el;
32606     },
32607
32608     /**
32609      * Trigger rendering of this TreePanel
32610      */
32611     render : function(){
32612         if (this.innerCt) {
32613             return this; // stop it rendering more than once!!
32614         }
32615         
32616         this.innerCt = this.el.createChild({tag:"ul",
32617                cls:"x-tree-root-ct " +
32618                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32619
32620         if(this.containerScroll){
32621             Roo.dd.ScrollManager.register(this.el);
32622         }
32623         if((this.enableDD || this.enableDrop) && !this.dropZone){
32624            /**
32625             * The dropZone used by this tree if drop is enabled
32626             * @type Roo.tree.TreeDropZone
32627             */
32628              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32629                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32630            });
32631         }
32632         if((this.enableDD || this.enableDrag) && !this.dragZone){
32633            /**
32634             * The dragZone used by this tree if drag is enabled
32635             * @type Roo.tree.TreeDragZone
32636             */
32637             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32638                ddGroup: this.ddGroup || "TreeDD",
32639                scroll: this.ddScroll
32640            });
32641         }
32642         this.getSelectionModel().init(this);
32643         if (!this.root) {
32644             Roo.log("ROOT not set in tree");
32645             return this;
32646         }
32647         this.root.render();
32648         if(!this.rootVisible){
32649             this.root.renderChildren();
32650         }
32651         return this;
32652     }
32653 });/*
32654  * Based on:
32655  * Ext JS Library 1.1.1
32656  * Copyright(c) 2006-2007, Ext JS, LLC.
32657  *
32658  * Originally Released Under LGPL - original licence link has changed is not relivant.
32659  *
32660  * Fork - LGPL
32661  * <script type="text/javascript">
32662  */
32663  
32664
32665 /**
32666  * @class Roo.tree.DefaultSelectionModel
32667  * @extends Roo.util.Observable
32668  * The default single selection for a TreePanel.
32669  * @param {Object} cfg Configuration
32670  */
32671 Roo.tree.DefaultSelectionModel = function(cfg){
32672    this.selNode = null;
32673    
32674    
32675    
32676    this.addEvents({
32677        /**
32678         * @event selectionchange
32679         * Fires when the selected node changes
32680         * @param {DefaultSelectionModel} this
32681         * @param {TreeNode} node the new selection
32682         */
32683        "selectionchange" : true,
32684
32685        /**
32686         * @event beforeselect
32687         * Fires before the selected node changes, return false to cancel the change
32688         * @param {DefaultSelectionModel} this
32689         * @param {TreeNode} node the new selection
32690         * @param {TreeNode} node the old selection
32691         */
32692        "beforeselect" : true
32693    });
32694    
32695     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32696 };
32697
32698 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32699     init : function(tree){
32700         this.tree = tree;
32701         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32702         tree.on("click", this.onNodeClick, this);
32703     },
32704     
32705     onNodeClick : function(node, e){
32706         if (e.ctrlKey && this.selNode == node)  {
32707             this.unselect(node);
32708             return;
32709         }
32710         this.select(node);
32711     },
32712     
32713     /**
32714      * Select a node.
32715      * @param {TreeNode} node The node to select
32716      * @return {TreeNode} The selected node
32717      */
32718     select : function(node){
32719         var last = this.selNode;
32720         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32721             if(last){
32722                 last.ui.onSelectedChange(false);
32723             }
32724             this.selNode = node;
32725             node.ui.onSelectedChange(true);
32726             this.fireEvent("selectionchange", this, node, last);
32727         }
32728         return node;
32729     },
32730     
32731     /**
32732      * Deselect a node.
32733      * @param {TreeNode} node The node to unselect
32734      */
32735     unselect : function(node){
32736         if(this.selNode == node){
32737             this.clearSelections();
32738         }    
32739     },
32740     
32741     /**
32742      * Clear all selections
32743      */
32744     clearSelections : function(){
32745         var n = this.selNode;
32746         if(n){
32747             n.ui.onSelectedChange(false);
32748             this.selNode = null;
32749             this.fireEvent("selectionchange", this, null);
32750         }
32751         return n;
32752     },
32753     
32754     /**
32755      * Get the selected node
32756      * @return {TreeNode} The selected node
32757      */
32758     getSelectedNode : function(){
32759         return this.selNode;    
32760     },
32761     
32762     /**
32763      * Returns true if the node is selected
32764      * @param {TreeNode} node The node to check
32765      * @return {Boolean}
32766      */
32767     isSelected : function(node){
32768         return this.selNode == node;  
32769     },
32770
32771     /**
32772      * Selects the node above the selected node in the tree, intelligently walking the nodes
32773      * @return TreeNode The new selection
32774      */
32775     selectPrevious : function(){
32776         var s = this.selNode || this.lastSelNode;
32777         if(!s){
32778             return null;
32779         }
32780         var ps = s.previousSibling;
32781         if(ps){
32782             if(!ps.isExpanded() || ps.childNodes.length < 1){
32783                 return this.select(ps);
32784             } else{
32785                 var lc = ps.lastChild;
32786                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32787                     lc = lc.lastChild;
32788                 }
32789                 return this.select(lc);
32790             }
32791         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32792             return this.select(s.parentNode);
32793         }
32794         return null;
32795     },
32796
32797     /**
32798      * Selects the node above the selected node in the tree, intelligently walking the nodes
32799      * @return TreeNode The new selection
32800      */
32801     selectNext : function(){
32802         var s = this.selNode || this.lastSelNode;
32803         if(!s){
32804             return null;
32805         }
32806         if(s.firstChild && s.isExpanded()){
32807              return this.select(s.firstChild);
32808          }else if(s.nextSibling){
32809              return this.select(s.nextSibling);
32810          }else if(s.parentNode){
32811             var newS = null;
32812             s.parentNode.bubble(function(){
32813                 if(this.nextSibling){
32814                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32815                     return false;
32816                 }
32817             });
32818             return newS;
32819          }
32820         return null;
32821     },
32822
32823     onKeyDown : function(e){
32824         var s = this.selNode || this.lastSelNode;
32825         // undesirable, but required
32826         var sm = this;
32827         if(!s){
32828             return;
32829         }
32830         var k = e.getKey();
32831         switch(k){
32832              case e.DOWN:
32833                  e.stopEvent();
32834                  this.selectNext();
32835              break;
32836              case e.UP:
32837                  e.stopEvent();
32838                  this.selectPrevious();
32839              break;
32840              case e.RIGHT:
32841                  e.preventDefault();
32842                  if(s.hasChildNodes()){
32843                      if(!s.isExpanded()){
32844                          s.expand();
32845                      }else if(s.firstChild){
32846                          this.select(s.firstChild, e);
32847                      }
32848                  }
32849              break;
32850              case e.LEFT:
32851                  e.preventDefault();
32852                  if(s.hasChildNodes() && s.isExpanded()){
32853                      s.collapse();
32854                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32855                      this.select(s.parentNode, e);
32856                  }
32857              break;
32858         };
32859     }
32860 });
32861
32862 /**
32863  * @class Roo.tree.MultiSelectionModel
32864  * @extends Roo.util.Observable
32865  * Multi selection for a TreePanel.
32866  * @param {Object} cfg Configuration
32867  */
32868 Roo.tree.MultiSelectionModel = function(){
32869    this.selNodes = [];
32870    this.selMap = {};
32871    this.addEvents({
32872        /**
32873         * @event selectionchange
32874         * Fires when the selected nodes change
32875         * @param {MultiSelectionModel} this
32876         * @param {Array} nodes Array of the selected nodes
32877         */
32878        "selectionchange" : true
32879    });
32880    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32881    
32882 };
32883
32884 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32885     init : function(tree){
32886         this.tree = tree;
32887         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32888         tree.on("click", this.onNodeClick, this);
32889     },
32890     
32891     onNodeClick : function(node, e){
32892         this.select(node, e, e.ctrlKey);
32893     },
32894     
32895     /**
32896      * Select a node.
32897      * @param {TreeNode} node The node to select
32898      * @param {EventObject} e (optional) An event associated with the selection
32899      * @param {Boolean} keepExisting True to retain existing selections
32900      * @return {TreeNode} The selected node
32901      */
32902     select : function(node, e, keepExisting){
32903         if(keepExisting !== true){
32904             this.clearSelections(true);
32905         }
32906         if(this.isSelected(node)){
32907             this.lastSelNode = node;
32908             return node;
32909         }
32910         this.selNodes.push(node);
32911         this.selMap[node.id] = node;
32912         this.lastSelNode = node;
32913         node.ui.onSelectedChange(true);
32914         this.fireEvent("selectionchange", this, this.selNodes);
32915         return node;
32916     },
32917     
32918     /**
32919      * Deselect a node.
32920      * @param {TreeNode} node The node to unselect
32921      */
32922     unselect : function(node){
32923         if(this.selMap[node.id]){
32924             node.ui.onSelectedChange(false);
32925             var sn = this.selNodes;
32926             var index = -1;
32927             if(sn.indexOf){
32928                 index = sn.indexOf(node);
32929             }else{
32930                 for(var i = 0, len = sn.length; i < len; i++){
32931                     if(sn[i] == node){
32932                         index = i;
32933                         break;
32934                     }
32935                 }
32936             }
32937             if(index != -1){
32938                 this.selNodes.splice(index, 1);
32939             }
32940             delete this.selMap[node.id];
32941             this.fireEvent("selectionchange", this, this.selNodes);
32942         }
32943     },
32944     
32945     /**
32946      * Clear all selections
32947      */
32948     clearSelections : function(suppressEvent){
32949         var sn = this.selNodes;
32950         if(sn.length > 0){
32951             for(var i = 0, len = sn.length; i < len; i++){
32952                 sn[i].ui.onSelectedChange(false);
32953             }
32954             this.selNodes = [];
32955             this.selMap = {};
32956             if(suppressEvent !== true){
32957                 this.fireEvent("selectionchange", this, this.selNodes);
32958             }
32959         }
32960     },
32961     
32962     /**
32963      * Returns true if the node is selected
32964      * @param {TreeNode} node The node to check
32965      * @return {Boolean}
32966      */
32967     isSelected : function(node){
32968         return this.selMap[node.id] ? true : false;  
32969     },
32970     
32971     /**
32972      * Returns an array of the selected nodes
32973      * @return {Array}
32974      */
32975     getSelectedNodes : function(){
32976         return this.selNodes;    
32977     },
32978
32979     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32980
32981     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32982
32983     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32984 });/*
32985  * Based on:
32986  * Ext JS Library 1.1.1
32987  * Copyright(c) 2006-2007, Ext JS, LLC.
32988  *
32989  * Originally Released Under LGPL - original licence link has changed is not relivant.
32990  *
32991  * Fork - LGPL
32992  * <script type="text/javascript">
32993  */
32994  
32995 /**
32996  * @class Roo.tree.TreeNode
32997  * @extends Roo.data.Node
32998  * @cfg {String} text The text for this node
32999  * @cfg {Boolean} expanded true to start the node expanded
33000  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33001  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33002  * @cfg {Boolean} disabled true to start the node disabled
33003  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33004  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33005  * @cfg {String} cls A css class to be added to the node
33006  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33007  * @cfg {String} href URL of the link used for the node (defaults to #)
33008  * @cfg {String} hrefTarget target frame for the link
33009  * @cfg {String} qtip An Ext QuickTip for the node
33010  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33011  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33012  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33013  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33014  * (defaults to undefined with no checkbox rendered)
33015  * @constructor
33016  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33017  */
33018 Roo.tree.TreeNode = function(attributes){
33019     attributes = attributes || {};
33020     if(typeof attributes == "string"){
33021         attributes = {text: attributes};
33022     }
33023     this.childrenRendered = false;
33024     this.rendered = false;
33025     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33026     this.expanded = attributes.expanded === true;
33027     this.isTarget = attributes.isTarget !== false;
33028     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33029     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33030
33031     /**
33032      * Read-only. The text for this node. To change it use setText().
33033      * @type String
33034      */
33035     this.text = attributes.text;
33036     /**
33037      * True if this node is disabled.
33038      * @type Boolean
33039      */
33040     this.disabled = attributes.disabled === true;
33041
33042     this.addEvents({
33043         /**
33044         * @event textchange
33045         * Fires when the text for this node is changed
33046         * @param {Node} this This node
33047         * @param {String} text The new text
33048         * @param {String} oldText The old text
33049         */
33050         "textchange" : true,
33051         /**
33052         * @event beforeexpand
33053         * Fires before this node is expanded, return false to cancel.
33054         * @param {Node} this This node
33055         * @param {Boolean} deep
33056         * @param {Boolean} anim
33057         */
33058         "beforeexpand" : true,
33059         /**
33060         * @event beforecollapse
33061         * Fires before this node is collapsed, return false to cancel.
33062         * @param {Node} this This node
33063         * @param {Boolean} deep
33064         * @param {Boolean} anim
33065         */
33066         "beforecollapse" : true,
33067         /**
33068         * @event expand
33069         * Fires when this node is expanded
33070         * @param {Node} this This node
33071         */
33072         "expand" : true,
33073         /**
33074         * @event disabledchange
33075         * Fires when the disabled status of this node changes
33076         * @param {Node} this This node
33077         * @param {Boolean} disabled
33078         */
33079         "disabledchange" : true,
33080         /**
33081         * @event collapse
33082         * Fires when this node is collapsed
33083         * @param {Node} this This node
33084         */
33085         "collapse" : true,
33086         /**
33087         * @event beforeclick
33088         * Fires before click processing. Return false to cancel the default action.
33089         * @param {Node} this This node
33090         * @param {Roo.EventObject} e The event object
33091         */
33092         "beforeclick":true,
33093         /**
33094         * @event checkchange
33095         * Fires when a node with a checkbox's checked property changes
33096         * @param {Node} this This node
33097         * @param {Boolean} checked
33098         */
33099         "checkchange":true,
33100         /**
33101         * @event click
33102         * Fires when this node is clicked
33103         * @param {Node} this This node
33104         * @param {Roo.EventObject} e The event object
33105         */
33106         "click":true,
33107         /**
33108         * @event dblclick
33109         * Fires when this node is double clicked
33110         * @param {Node} this This node
33111         * @param {Roo.EventObject} e The event object
33112         */
33113         "dblclick":true,
33114         /**
33115         * @event contextmenu
33116         * Fires when this node is right clicked
33117         * @param {Node} this This node
33118         * @param {Roo.EventObject} e The event object
33119         */
33120         "contextmenu":true,
33121         /**
33122         * @event beforechildrenrendered
33123         * Fires right before the child nodes for this node are rendered
33124         * @param {Node} this This node
33125         */
33126         "beforechildrenrendered":true
33127     });
33128
33129     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33130
33131     /**
33132      * Read-only. The UI for this node
33133      * @type TreeNodeUI
33134      */
33135     this.ui = new uiClass(this);
33136     
33137     // finally support items[]
33138     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33139         return;
33140     }
33141     
33142     
33143     Roo.each(this.attributes.items, function(c) {
33144         this.appendChild(Roo.factory(c,Roo.Tree));
33145     }, this);
33146     delete this.attributes.items;
33147     
33148     
33149     
33150 };
33151 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33152     preventHScroll: true,
33153     /**
33154      * Returns true if this node is expanded
33155      * @return {Boolean}
33156      */
33157     isExpanded : function(){
33158         return this.expanded;
33159     },
33160
33161     /**
33162      * Returns the UI object for this node
33163      * @return {TreeNodeUI}
33164      */
33165     getUI : function(){
33166         return this.ui;
33167     },
33168
33169     // private override
33170     setFirstChild : function(node){
33171         var of = this.firstChild;
33172         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33173         if(this.childrenRendered && of && node != of){
33174             of.renderIndent(true, true);
33175         }
33176         if(this.rendered){
33177             this.renderIndent(true, true);
33178         }
33179     },
33180
33181     // private override
33182     setLastChild : function(node){
33183         var ol = this.lastChild;
33184         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33185         if(this.childrenRendered && ol && node != ol){
33186             ol.renderIndent(true, true);
33187         }
33188         if(this.rendered){
33189             this.renderIndent(true, true);
33190         }
33191     },
33192
33193     // these methods are overridden to provide lazy rendering support
33194     // private override
33195     appendChild : function()
33196     {
33197         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33198         if(node && this.childrenRendered){
33199             node.render();
33200         }
33201         this.ui.updateExpandIcon();
33202         return node;
33203     },
33204
33205     // private override
33206     removeChild : function(node){
33207         this.ownerTree.getSelectionModel().unselect(node);
33208         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33209         // if it's been rendered remove dom node
33210         if(this.childrenRendered){
33211             node.ui.remove();
33212         }
33213         if(this.childNodes.length < 1){
33214             this.collapse(false, false);
33215         }else{
33216             this.ui.updateExpandIcon();
33217         }
33218         if(!this.firstChild) {
33219             this.childrenRendered = false;
33220         }
33221         return node;
33222     },
33223
33224     // private override
33225     insertBefore : function(node, refNode){
33226         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33227         if(newNode && refNode && this.childrenRendered){
33228             node.render();
33229         }
33230         this.ui.updateExpandIcon();
33231         return newNode;
33232     },
33233
33234     /**
33235      * Sets the text for this node
33236      * @param {String} text
33237      */
33238     setText : function(text){
33239         var oldText = this.text;
33240         this.text = text;
33241         this.attributes.text = text;
33242         if(this.rendered){ // event without subscribing
33243             this.ui.onTextChange(this, text, oldText);
33244         }
33245         this.fireEvent("textchange", this, text, oldText);
33246     },
33247
33248     /**
33249      * Triggers selection of this node
33250      */
33251     select : function(){
33252         this.getOwnerTree().getSelectionModel().select(this);
33253     },
33254
33255     /**
33256      * Triggers deselection of this node
33257      */
33258     unselect : function(){
33259         this.getOwnerTree().getSelectionModel().unselect(this);
33260     },
33261
33262     /**
33263      * Returns true if this node is selected
33264      * @return {Boolean}
33265      */
33266     isSelected : function(){
33267         return this.getOwnerTree().getSelectionModel().isSelected(this);
33268     },
33269
33270     /**
33271      * Expand this node.
33272      * @param {Boolean} deep (optional) True to expand all children as well
33273      * @param {Boolean} anim (optional) false to cancel the default animation
33274      * @param {Function} callback (optional) A callback to be called when
33275      * expanding this node completes (does not wait for deep expand to complete).
33276      * Called with 1 parameter, this node.
33277      */
33278     expand : function(deep, anim, callback){
33279         if(!this.expanded){
33280             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33281                 return;
33282             }
33283             if(!this.childrenRendered){
33284                 this.renderChildren();
33285             }
33286             this.expanded = true;
33287             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33288                 this.ui.animExpand(function(){
33289                     this.fireEvent("expand", this);
33290                     if(typeof callback == "function"){
33291                         callback(this);
33292                     }
33293                     if(deep === true){
33294                         this.expandChildNodes(true);
33295                     }
33296                 }.createDelegate(this));
33297                 return;
33298             }else{
33299                 this.ui.expand();
33300                 this.fireEvent("expand", this);
33301                 if(typeof callback == "function"){
33302                     callback(this);
33303                 }
33304             }
33305         }else{
33306            if(typeof callback == "function"){
33307                callback(this);
33308            }
33309         }
33310         if(deep === true){
33311             this.expandChildNodes(true);
33312         }
33313     },
33314
33315     isHiddenRoot : function(){
33316         return this.isRoot && !this.getOwnerTree().rootVisible;
33317     },
33318
33319     /**
33320      * Collapse this node.
33321      * @param {Boolean} deep (optional) True to collapse all children as well
33322      * @param {Boolean} anim (optional) false to cancel the default animation
33323      */
33324     collapse : function(deep, anim){
33325         if(this.expanded && !this.isHiddenRoot()){
33326             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33327                 return;
33328             }
33329             this.expanded = false;
33330             if((this.getOwnerTree().animate && anim !== false) || anim){
33331                 this.ui.animCollapse(function(){
33332                     this.fireEvent("collapse", this);
33333                     if(deep === true){
33334                         this.collapseChildNodes(true);
33335                     }
33336                 }.createDelegate(this));
33337                 return;
33338             }else{
33339                 this.ui.collapse();
33340                 this.fireEvent("collapse", this);
33341             }
33342         }
33343         if(deep === true){
33344             var cs = this.childNodes;
33345             for(var i = 0, len = cs.length; i < len; i++) {
33346                 cs[i].collapse(true, false);
33347             }
33348         }
33349     },
33350
33351     // private
33352     delayedExpand : function(delay){
33353         if(!this.expandProcId){
33354             this.expandProcId = this.expand.defer(delay, this);
33355         }
33356     },
33357
33358     // private
33359     cancelExpand : function(){
33360         if(this.expandProcId){
33361             clearTimeout(this.expandProcId);
33362         }
33363         this.expandProcId = false;
33364     },
33365
33366     /**
33367      * Toggles expanded/collapsed state of the node
33368      */
33369     toggle : function(){
33370         if(this.expanded){
33371             this.collapse();
33372         }else{
33373             this.expand();
33374         }
33375     },
33376
33377     /**
33378      * Ensures all parent nodes are expanded
33379      */
33380     ensureVisible : function(callback){
33381         var tree = this.getOwnerTree();
33382         tree.expandPath(this.parentNode.getPath(), false, function(){
33383             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33384             Roo.callback(callback);
33385         }.createDelegate(this));
33386     },
33387
33388     /**
33389      * Expand all child nodes
33390      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33391      */
33392     expandChildNodes : function(deep){
33393         var cs = this.childNodes;
33394         for(var i = 0, len = cs.length; i < len; i++) {
33395                 cs[i].expand(deep);
33396         }
33397     },
33398
33399     /**
33400      * Collapse all child nodes
33401      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33402      */
33403     collapseChildNodes : function(deep){
33404         var cs = this.childNodes;
33405         for(var i = 0, len = cs.length; i < len; i++) {
33406                 cs[i].collapse(deep);
33407         }
33408     },
33409
33410     /**
33411      * Disables this node
33412      */
33413     disable : function(){
33414         this.disabled = true;
33415         this.unselect();
33416         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33417             this.ui.onDisableChange(this, true);
33418         }
33419         this.fireEvent("disabledchange", this, true);
33420     },
33421
33422     /**
33423      * Enables this node
33424      */
33425     enable : function(){
33426         this.disabled = false;
33427         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33428             this.ui.onDisableChange(this, false);
33429         }
33430         this.fireEvent("disabledchange", this, false);
33431     },
33432
33433     // private
33434     renderChildren : function(suppressEvent){
33435         if(suppressEvent !== false){
33436             this.fireEvent("beforechildrenrendered", this);
33437         }
33438         var cs = this.childNodes;
33439         for(var i = 0, len = cs.length; i < len; i++){
33440             cs[i].render(true);
33441         }
33442         this.childrenRendered = true;
33443     },
33444
33445     // private
33446     sort : function(fn, scope){
33447         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33448         if(this.childrenRendered){
33449             var cs = this.childNodes;
33450             for(var i = 0, len = cs.length; i < len; i++){
33451                 cs[i].render(true);
33452             }
33453         }
33454     },
33455
33456     // private
33457     render : function(bulkRender){
33458         this.ui.render(bulkRender);
33459         if(!this.rendered){
33460             this.rendered = true;
33461             if(this.expanded){
33462                 this.expanded = false;
33463                 this.expand(false, false);
33464             }
33465         }
33466     },
33467
33468     // private
33469     renderIndent : function(deep, refresh){
33470         if(refresh){
33471             this.ui.childIndent = null;
33472         }
33473         this.ui.renderIndent();
33474         if(deep === true && this.childrenRendered){
33475             var cs = this.childNodes;
33476             for(var i = 0, len = cs.length; i < len; i++){
33477                 cs[i].renderIndent(true, refresh);
33478             }
33479         }
33480     }
33481 });/*
33482  * Based on:
33483  * Ext JS Library 1.1.1
33484  * Copyright(c) 2006-2007, Ext JS, LLC.
33485  *
33486  * Originally Released Under LGPL - original licence link has changed is not relivant.
33487  *
33488  * Fork - LGPL
33489  * <script type="text/javascript">
33490  */
33491  
33492 /**
33493  * @class Roo.tree.AsyncTreeNode
33494  * @extends Roo.tree.TreeNode
33495  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33496  * @constructor
33497  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33498  */
33499  Roo.tree.AsyncTreeNode = function(config){
33500     this.loaded = false;
33501     this.loading = false;
33502     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33503     /**
33504     * @event beforeload
33505     * Fires before this node is loaded, return false to cancel
33506     * @param {Node} this This node
33507     */
33508     this.addEvents({'beforeload':true, 'load': true});
33509     /**
33510     * @event load
33511     * Fires when this node is loaded
33512     * @param {Node} this This node
33513     */
33514     /**
33515      * The loader used by this node (defaults to using the tree's defined loader)
33516      * @type TreeLoader
33517      * @property loader
33518      */
33519 };
33520 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33521     expand : function(deep, anim, callback){
33522         if(this.loading){ // if an async load is already running, waiting til it's done
33523             var timer;
33524             var f = function(){
33525                 if(!this.loading){ // done loading
33526                     clearInterval(timer);
33527                     this.expand(deep, anim, callback);
33528                 }
33529             }.createDelegate(this);
33530             timer = setInterval(f, 200);
33531             return;
33532         }
33533         if(!this.loaded){
33534             if(this.fireEvent("beforeload", this) === false){
33535                 return;
33536             }
33537             this.loading = true;
33538             this.ui.beforeLoad(this);
33539             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33540             if(loader){
33541                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33542                 return;
33543             }
33544         }
33545         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33546     },
33547     
33548     /**
33549      * Returns true if this node is currently loading
33550      * @return {Boolean}
33551      */
33552     isLoading : function(){
33553         return this.loading;  
33554     },
33555     
33556     loadComplete : function(deep, anim, callback){
33557         this.loading = false;
33558         this.loaded = true;
33559         this.ui.afterLoad(this);
33560         this.fireEvent("load", this);
33561         this.expand(deep, anim, callback);
33562     },
33563     
33564     /**
33565      * Returns true if this node has been loaded
33566      * @return {Boolean}
33567      */
33568     isLoaded : function(){
33569         return this.loaded;
33570     },
33571     
33572     hasChildNodes : function(){
33573         if(!this.isLeaf() && !this.loaded){
33574             return true;
33575         }else{
33576             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33577         }
33578     },
33579
33580     /**
33581      * Trigger a reload for this node
33582      * @param {Function} callback
33583      */
33584     reload : function(callback){
33585         this.collapse(false, false);
33586         while(this.firstChild){
33587             this.removeChild(this.firstChild);
33588         }
33589         this.childrenRendered = false;
33590         this.loaded = false;
33591         if(this.isHiddenRoot()){
33592             this.expanded = false;
33593         }
33594         this.expand(false, false, callback);
33595     }
33596 });/*
33597  * Based on:
33598  * Ext JS Library 1.1.1
33599  * Copyright(c) 2006-2007, Ext JS, LLC.
33600  *
33601  * Originally Released Under LGPL - original licence link has changed is not relivant.
33602  *
33603  * Fork - LGPL
33604  * <script type="text/javascript">
33605  */
33606  
33607 /**
33608  * @class Roo.tree.TreeNodeUI
33609  * @constructor
33610  * @param {Object} node The node to render
33611  * The TreeNode UI implementation is separate from the
33612  * tree implementation. Unless you are customizing the tree UI,
33613  * you should never have to use this directly.
33614  */
33615 Roo.tree.TreeNodeUI = function(node){
33616     this.node = node;
33617     this.rendered = false;
33618     this.animating = false;
33619     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33620 };
33621
33622 Roo.tree.TreeNodeUI.prototype = {
33623     removeChild : function(node){
33624         if(this.rendered){
33625             this.ctNode.removeChild(node.ui.getEl());
33626         }
33627     },
33628
33629     beforeLoad : function(){
33630          this.addClass("x-tree-node-loading");
33631     },
33632
33633     afterLoad : function(){
33634          this.removeClass("x-tree-node-loading");
33635     },
33636
33637     onTextChange : function(node, text, oldText){
33638         if(this.rendered){
33639             this.textNode.innerHTML = text;
33640         }
33641     },
33642
33643     onDisableChange : function(node, state){
33644         this.disabled = state;
33645         if(state){
33646             this.addClass("x-tree-node-disabled");
33647         }else{
33648             this.removeClass("x-tree-node-disabled");
33649         }
33650     },
33651
33652     onSelectedChange : function(state){
33653         if(state){
33654             this.focus();
33655             this.addClass("x-tree-selected");
33656         }else{
33657             //this.blur();
33658             this.removeClass("x-tree-selected");
33659         }
33660     },
33661
33662     onMove : function(tree, node, oldParent, newParent, index, refNode){
33663         this.childIndent = null;
33664         if(this.rendered){
33665             var targetNode = newParent.ui.getContainer();
33666             if(!targetNode){//target not rendered
33667                 this.holder = document.createElement("div");
33668                 this.holder.appendChild(this.wrap);
33669                 return;
33670             }
33671             var insertBefore = refNode ? refNode.ui.getEl() : null;
33672             if(insertBefore){
33673                 targetNode.insertBefore(this.wrap, insertBefore);
33674             }else{
33675                 targetNode.appendChild(this.wrap);
33676             }
33677             this.node.renderIndent(true);
33678         }
33679     },
33680
33681     addClass : function(cls){
33682         if(this.elNode){
33683             Roo.fly(this.elNode).addClass(cls);
33684         }
33685     },
33686
33687     removeClass : function(cls){
33688         if(this.elNode){
33689             Roo.fly(this.elNode).removeClass(cls);
33690         }
33691     },
33692
33693     remove : function(){
33694         if(this.rendered){
33695             this.holder = document.createElement("div");
33696             this.holder.appendChild(this.wrap);
33697         }
33698     },
33699
33700     fireEvent : function(){
33701         return this.node.fireEvent.apply(this.node, arguments);
33702     },
33703
33704     initEvents : function(){
33705         this.node.on("move", this.onMove, this);
33706         var E = Roo.EventManager;
33707         var a = this.anchor;
33708
33709         var el = Roo.fly(a, '_treeui');
33710
33711         if(Roo.isOpera){ // opera render bug ignores the CSS
33712             el.setStyle("text-decoration", "none");
33713         }
33714
33715         el.on("click", this.onClick, this);
33716         el.on("dblclick", this.onDblClick, this);
33717
33718         if(this.checkbox){
33719             Roo.EventManager.on(this.checkbox,
33720                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33721         }
33722
33723         el.on("contextmenu", this.onContextMenu, this);
33724
33725         var icon = Roo.fly(this.iconNode);
33726         icon.on("click", this.onClick, this);
33727         icon.on("dblclick", this.onDblClick, this);
33728         icon.on("contextmenu", this.onContextMenu, this);
33729         E.on(this.ecNode, "click", this.ecClick, this, true);
33730
33731         if(this.node.disabled){
33732             this.addClass("x-tree-node-disabled");
33733         }
33734         if(this.node.hidden){
33735             this.addClass("x-tree-node-disabled");
33736         }
33737         var ot = this.node.getOwnerTree();
33738         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33739         if(dd && (!this.node.isRoot || ot.rootVisible)){
33740             Roo.dd.Registry.register(this.elNode, {
33741                 node: this.node,
33742                 handles: this.getDDHandles(),
33743                 isHandle: false
33744             });
33745         }
33746     },
33747
33748     getDDHandles : function(){
33749         return [this.iconNode, this.textNode];
33750     },
33751
33752     hide : function(){
33753         if(this.rendered){
33754             this.wrap.style.display = "none";
33755         }
33756     },
33757
33758     show : function(){
33759         if(this.rendered){
33760             this.wrap.style.display = "";
33761         }
33762     },
33763
33764     onContextMenu : function(e){
33765         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33766             e.preventDefault();
33767             this.focus();
33768             this.fireEvent("contextmenu", this.node, e);
33769         }
33770     },
33771
33772     onClick : function(e){
33773         if(this.dropping){
33774             e.stopEvent();
33775             return;
33776         }
33777         if(this.fireEvent("beforeclick", this.node, e) !== false){
33778             if(!this.disabled && this.node.attributes.href){
33779                 this.fireEvent("click", this.node, e);
33780                 return;
33781             }
33782             e.preventDefault();
33783             if(this.disabled){
33784                 return;
33785             }
33786
33787             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33788                 this.node.toggle();
33789             }
33790
33791             this.fireEvent("click", this.node, e);
33792         }else{
33793             e.stopEvent();
33794         }
33795     },
33796
33797     onDblClick : function(e){
33798         e.preventDefault();
33799         if(this.disabled){
33800             return;
33801         }
33802         if(this.checkbox){
33803             this.toggleCheck();
33804         }
33805         if(!this.animating && this.node.hasChildNodes()){
33806             this.node.toggle();
33807         }
33808         this.fireEvent("dblclick", this.node, e);
33809     },
33810
33811     onCheckChange : function(){
33812         var checked = this.checkbox.checked;
33813         this.node.attributes.checked = checked;
33814         this.fireEvent('checkchange', this.node, checked);
33815     },
33816
33817     ecClick : function(e){
33818         if(!this.animating && this.node.hasChildNodes()){
33819             this.node.toggle();
33820         }
33821     },
33822
33823     startDrop : function(){
33824         this.dropping = true;
33825     },
33826
33827     // delayed drop so the click event doesn't get fired on a drop
33828     endDrop : function(){
33829        setTimeout(function(){
33830            this.dropping = false;
33831        }.createDelegate(this), 50);
33832     },
33833
33834     expand : function(){
33835         this.updateExpandIcon();
33836         this.ctNode.style.display = "";
33837     },
33838
33839     focus : function(){
33840         if(!this.node.preventHScroll){
33841             try{this.anchor.focus();
33842             }catch(e){}
33843         }else if(!Roo.isIE){
33844             try{
33845                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33846                 var l = noscroll.scrollLeft;
33847                 this.anchor.focus();
33848                 noscroll.scrollLeft = l;
33849             }catch(e){}
33850         }
33851     },
33852
33853     toggleCheck : function(value){
33854         var cb = this.checkbox;
33855         if(cb){
33856             cb.checked = (value === undefined ? !cb.checked : value);
33857         }
33858     },
33859
33860     blur : function(){
33861         try{
33862             this.anchor.blur();
33863         }catch(e){}
33864     },
33865
33866     animExpand : function(callback){
33867         var ct = Roo.get(this.ctNode);
33868         ct.stopFx();
33869         if(!this.node.hasChildNodes()){
33870             this.updateExpandIcon();
33871             this.ctNode.style.display = "";
33872             Roo.callback(callback);
33873             return;
33874         }
33875         this.animating = true;
33876         this.updateExpandIcon();
33877
33878         ct.slideIn('t', {
33879            callback : function(){
33880                this.animating = false;
33881                Roo.callback(callback);
33882             },
33883             scope: this,
33884             duration: this.node.ownerTree.duration || .25
33885         });
33886     },
33887
33888     highlight : function(){
33889         var tree = this.node.getOwnerTree();
33890         Roo.fly(this.wrap).highlight(
33891             tree.hlColor || "C3DAF9",
33892             {endColor: tree.hlBaseColor}
33893         );
33894     },
33895
33896     collapse : function(){
33897         this.updateExpandIcon();
33898         this.ctNode.style.display = "none";
33899     },
33900
33901     animCollapse : function(callback){
33902         var ct = Roo.get(this.ctNode);
33903         ct.enableDisplayMode('block');
33904         ct.stopFx();
33905
33906         this.animating = true;
33907         this.updateExpandIcon();
33908
33909         ct.slideOut('t', {
33910             callback : function(){
33911                this.animating = false;
33912                Roo.callback(callback);
33913             },
33914             scope: this,
33915             duration: this.node.ownerTree.duration || .25
33916         });
33917     },
33918
33919     getContainer : function(){
33920         return this.ctNode;
33921     },
33922
33923     getEl : function(){
33924         return this.wrap;
33925     },
33926
33927     appendDDGhost : function(ghostNode){
33928         ghostNode.appendChild(this.elNode.cloneNode(true));
33929     },
33930
33931     getDDRepairXY : function(){
33932         return Roo.lib.Dom.getXY(this.iconNode);
33933     },
33934
33935     onRender : function(){
33936         this.render();
33937     },
33938
33939     render : function(bulkRender){
33940         var n = this.node, a = n.attributes;
33941         var targetNode = n.parentNode ?
33942               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33943
33944         if(!this.rendered){
33945             this.rendered = true;
33946
33947             this.renderElements(n, a, targetNode, bulkRender);
33948
33949             if(a.qtip){
33950                if(this.textNode.setAttributeNS){
33951                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33952                    if(a.qtipTitle){
33953                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33954                    }
33955                }else{
33956                    this.textNode.setAttribute("ext:qtip", a.qtip);
33957                    if(a.qtipTitle){
33958                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33959                    }
33960                }
33961             }else if(a.qtipCfg){
33962                 a.qtipCfg.target = Roo.id(this.textNode);
33963                 Roo.QuickTips.register(a.qtipCfg);
33964             }
33965             this.initEvents();
33966             if(!this.node.expanded){
33967                 this.updateExpandIcon();
33968             }
33969         }else{
33970             if(bulkRender === true) {
33971                 targetNode.appendChild(this.wrap);
33972             }
33973         }
33974     },
33975
33976     renderElements : function(n, a, targetNode, bulkRender)
33977     {
33978         // add some indent caching, this helps performance when rendering a large tree
33979         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33980         var t = n.getOwnerTree();
33981         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33982         if (typeof(n.attributes.html) != 'undefined') {
33983             txt = n.attributes.html;
33984         }
33985         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33986         var cb = typeof a.checked == 'boolean';
33987         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33988         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33989             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33990             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33991             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33992             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33993             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33994              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33995                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33996             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33997             "</li>"];
33998
33999         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34000             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34001                                 n.nextSibling.ui.getEl(), buf.join(""));
34002         }else{
34003             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34004         }
34005
34006         this.elNode = this.wrap.childNodes[0];
34007         this.ctNode = this.wrap.childNodes[1];
34008         var cs = this.elNode.childNodes;
34009         this.indentNode = cs[0];
34010         this.ecNode = cs[1];
34011         this.iconNode = cs[2];
34012         var index = 3;
34013         if(cb){
34014             this.checkbox = cs[3];
34015             index++;
34016         }
34017         this.anchor = cs[index];
34018         this.textNode = cs[index].firstChild;
34019     },
34020
34021     getAnchor : function(){
34022         return this.anchor;
34023     },
34024
34025     getTextEl : function(){
34026         return this.textNode;
34027     },
34028
34029     getIconEl : function(){
34030         return this.iconNode;
34031     },
34032
34033     isChecked : function(){
34034         return this.checkbox ? this.checkbox.checked : false;
34035     },
34036
34037     updateExpandIcon : function(){
34038         if(this.rendered){
34039             var n = this.node, c1, c2;
34040             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34041             var hasChild = n.hasChildNodes();
34042             if(hasChild){
34043                 if(n.expanded){
34044                     cls += "-minus";
34045                     c1 = "x-tree-node-collapsed";
34046                     c2 = "x-tree-node-expanded";
34047                 }else{
34048                     cls += "-plus";
34049                     c1 = "x-tree-node-expanded";
34050                     c2 = "x-tree-node-collapsed";
34051                 }
34052                 if(this.wasLeaf){
34053                     this.removeClass("x-tree-node-leaf");
34054                     this.wasLeaf = false;
34055                 }
34056                 if(this.c1 != c1 || this.c2 != c2){
34057                     Roo.fly(this.elNode).replaceClass(c1, c2);
34058                     this.c1 = c1; this.c2 = c2;
34059                 }
34060             }else{
34061                 // this changes non-leafs into leafs if they have no children.
34062                 // it's not very rational behaviour..
34063                 
34064                 if(!this.wasLeaf && this.node.leaf){
34065                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34066                     delete this.c1;
34067                     delete this.c2;
34068                     this.wasLeaf = true;
34069                 }
34070             }
34071             var ecc = "x-tree-ec-icon "+cls;
34072             if(this.ecc != ecc){
34073                 this.ecNode.className = ecc;
34074                 this.ecc = ecc;
34075             }
34076         }
34077     },
34078
34079     getChildIndent : function(){
34080         if(!this.childIndent){
34081             var buf = [];
34082             var p = this.node;
34083             while(p){
34084                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34085                     if(!p.isLast()) {
34086                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34087                     } else {
34088                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34089                     }
34090                 }
34091                 p = p.parentNode;
34092             }
34093             this.childIndent = buf.join("");
34094         }
34095         return this.childIndent;
34096     },
34097
34098     renderIndent : function(){
34099         if(this.rendered){
34100             var indent = "";
34101             var p = this.node.parentNode;
34102             if(p){
34103                 indent = p.ui.getChildIndent();
34104             }
34105             if(this.indentMarkup != indent){ // don't rerender if not required
34106                 this.indentNode.innerHTML = indent;
34107                 this.indentMarkup = indent;
34108             }
34109             this.updateExpandIcon();
34110         }
34111     }
34112 };
34113
34114 Roo.tree.RootTreeNodeUI = function(){
34115     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34116 };
34117 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34118     render : function(){
34119         if(!this.rendered){
34120             var targetNode = this.node.ownerTree.innerCt.dom;
34121             this.node.expanded = true;
34122             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34123             this.wrap = this.ctNode = targetNode.firstChild;
34124         }
34125     },
34126     collapse : function(){
34127     },
34128     expand : function(){
34129     }
34130 });/*
34131  * Based on:
34132  * Ext JS Library 1.1.1
34133  * Copyright(c) 2006-2007, Ext JS, LLC.
34134  *
34135  * Originally Released Under LGPL - original licence link has changed is not relivant.
34136  *
34137  * Fork - LGPL
34138  * <script type="text/javascript">
34139  */
34140 /**
34141  * @class Roo.tree.TreeLoader
34142  * @extends Roo.util.Observable
34143  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34144  * nodes from a specified URL. The response must be a javascript Array definition
34145  * who's elements are node definition objects. eg:
34146  * <pre><code>
34147 {  success : true,
34148    data :      [
34149    
34150     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34151     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34152     ]
34153 }
34154
34155
34156 </code></pre>
34157  * <br><br>
34158  * The old style respose with just an array is still supported, but not recommended.
34159  * <br><br>
34160  *
34161  * A server request is sent, and child nodes are loaded only when a node is expanded.
34162  * The loading node's id is passed to the server under the parameter name "node" to
34163  * enable the server to produce the correct child nodes.
34164  * <br><br>
34165  * To pass extra parameters, an event handler may be attached to the "beforeload"
34166  * event, and the parameters specified in the TreeLoader's baseParams property:
34167  * <pre><code>
34168     myTreeLoader.on("beforeload", function(treeLoader, node) {
34169         this.baseParams.category = node.attributes.category;
34170     }, this);
34171 </code></pre><
34172  * This would pass an HTTP parameter called "category" to the server containing
34173  * the value of the Node's "category" attribute.
34174  * @constructor
34175  * Creates a new Treeloader.
34176  * @param {Object} config A config object containing config properties.
34177  */
34178 Roo.tree.TreeLoader = function(config){
34179     this.baseParams = {};
34180     this.requestMethod = "POST";
34181     Roo.apply(this, config);
34182
34183     this.addEvents({
34184     
34185         /**
34186          * @event beforeload
34187          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34188          * @param {Object} This TreeLoader object.
34189          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34190          * @param {Object} callback The callback function specified in the {@link #load} call.
34191          */
34192         beforeload : true,
34193         /**
34194          * @event load
34195          * Fires when the node has been successfuly loaded.
34196          * @param {Object} This TreeLoader object.
34197          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34198          * @param {Object} response The response object containing the data from the server.
34199          */
34200         load : true,
34201         /**
34202          * @event loadexception
34203          * Fires if the network request failed.
34204          * @param {Object} This TreeLoader object.
34205          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34206          * @param {Object} response The response object containing the data from the server.
34207          */
34208         loadexception : true,
34209         /**
34210          * @event create
34211          * Fires before a node is created, enabling you to return custom Node types 
34212          * @param {Object} This TreeLoader object.
34213          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34214          */
34215         create : true
34216     });
34217
34218     Roo.tree.TreeLoader.superclass.constructor.call(this);
34219 };
34220
34221 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34222     /**
34223     * @cfg {String} dataUrl The URL from which to request a Json string which
34224     * specifies an array of node definition object representing the child nodes
34225     * to be loaded.
34226     */
34227     /**
34228     * @cfg {String} requestMethod either GET or POST
34229     * defaults to POST (due to BC)
34230     * to be loaded.
34231     */
34232     /**
34233     * @cfg {Object} baseParams (optional) An object containing properties which
34234     * specify HTTP parameters to be passed to each request for child nodes.
34235     */
34236     /**
34237     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34238     * created by this loader. If the attributes sent by the server have an attribute in this object,
34239     * they take priority.
34240     */
34241     /**
34242     * @cfg {Object} uiProviders (optional) An object containing properties which
34243     * 
34244     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34245     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34246     * <i>uiProvider</i> attribute of a returned child node is a string rather
34247     * than a reference to a TreeNodeUI implementation, this that string value
34248     * is used as a property name in the uiProviders object. You can define the provider named
34249     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34250     */
34251     uiProviders : {},
34252
34253     /**
34254     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34255     * child nodes before loading.
34256     */
34257     clearOnLoad : true,
34258
34259     /**
34260     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34261     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34262     * Grid query { data : [ .....] }
34263     */
34264     
34265     root : false,
34266      /**
34267     * @cfg {String} queryParam (optional) 
34268     * Name of the query as it will be passed on the querystring (defaults to 'node')
34269     * eg. the request will be ?node=[id]
34270     */
34271     
34272     
34273     queryParam: false,
34274     
34275     /**
34276      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34277      * This is called automatically when a node is expanded, but may be used to reload
34278      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34279      * @param {Roo.tree.TreeNode} node
34280      * @param {Function} callback
34281      */
34282     load : function(node, callback){
34283         if(this.clearOnLoad){
34284             while(node.firstChild){
34285                 node.removeChild(node.firstChild);
34286             }
34287         }
34288         if(node.attributes.children){ // preloaded json children
34289             var cs = node.attributes.children;
34290             for(var i = 0, len = cs.length; i < len; i++){
34291                 node.appendChild(this.createNode(cs[i]));
34292             }
34293             if(typeof callback == "function"){
34294                 callback();
34295             }
34296         }else if(this.dataUrl){
34297             this.requestData(node, callback);
34298         }
34299     },
34300
34301     getParams: function(node){
34302         var buf = [], bp = this.baseParams;
34303         for(var key in bp){
34304             if(typeof bp[key] != "function"){
34305                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34306             }
34307         }
34308         var n = this.queryParam === false ? 'node' : this.queryParam;
34309         buf.push(n + "=", encodeURIComponent(node.id));
34310         return buf.join("");
34311     },
34312
34313     requestData : function(node, callback){
34314         if(this.fireEvent("beforeload", this, node, callback) !== false){
34315             this.transId = Roo.Ajax.request({
34316                 method:this.requestMethod,
34317                 url: this.dataUrl||this.url,
34318                 success: this.handleResponse,
34319                 failure: this.handleFailure,
34320                 scope: this,
34321                 argument: {callback: callback, node: node},
34322                 params: this.getParams(node)
34323             });
34324         }else{
34325             // if the load is cancelled, make sure we notify
34326             // the node that we are done
34327             if(typeof callback == "function"){
34328                 callback();
34329             }
34330         }
34331     },
34332
34333     isLoading : function(){
34334         return this.transId ? true : false;
34335     },
34336
34337     abort : function(){
34338         if(this.isLoading()){
34339             Roo.Ajax.abort(this.transId);
34340         }
34341     },
34342
34343     // private
34344     createNode : function(attr)
34345     {
34346         // apply baseAttrs, nice idea Corey!
34347         if(this.baseAttrs){
34348             Roo.applyIf(attr, this.baseAttrs);
34349         }
34350         if(this.applyLoader !== false){
34351             attr.loader = this;
34352         }
34353         // uiProvider = depreciated..
34354         
34355         if(typeof(attr.uiProvider) == 'string'){
34356            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34357                 /**  eval:var:attr */ eval(attr.uiProvider);
34358         }
34359         if(typeof(this.uiProviders['default']) != 'undefined') {
34360             attr.uiProvider = this.uiProviders['default'];
34361         }
34362         
34363         this.fireEvent('create', this, attr);
34364         
34365         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34366         return(attr.leaf ?
34367                         new Roo.tree.TreeNode(attr) :
34368                         new Roo.tree.AsyncTreeNode(attr));
34369     },
34370
34371     processResponse : function(response, node, callback)
34372     {
34373         var json = response.responseText;
34374         try {
34375             
34376             var o = Roo.decode(json);
34377             
34378             if (this.root === false && typeof(o.success) != undefined) {
34379                 this.root = 'data'; // the default behaviour for list like data..
34380                 }
34381                 
34382             if (this.root !== false &&  !o.success) {
34383                 // it's a failure condition.
34384                 var a = response.argument;
34385                 this.fireEvent("loadexception", this, a.node, response);
34386                 Roo.log("Load failed - should have a handler really");
34387                 return;
34388             }
34389             
34390             
34391             
34392             if (this.root !== false) {
34393                  o = o[this.root];
34394             }
34395             
34396             for(var i = 0, len = o.length; i < len; i++){
34397                 var n = this.createNode(o[i]);
34398                 if(n){
34399                     node.appendChild(n);
34400                 }
34401             }
34402             if(typeof callback == "function"){
34403                 callback(this, node);
34404             }
34405         }catch(e){
34406             this.handleFailure(response);
34407         }
34408     },
34409
34410     handleResponse : function(response){
34411         this.transId = false;
34412         var a = response.argument;
34413         this.processResponse(response, a.node, a.callback);
34414         this.fireEvent("load", this, a.node, response);
34415     },
34416
34417     handleFailure : function(response)
34418     {
34419         // should handle failure better..
34420         this.transId = false;
34421         var a = response.argument;
34422         this.fireEvent("loadexception", this, a.node, response);
34423         if(typeof a.callback == "function"){
34424             a.callback(this, a.node);
34425         }
34426     }
34427 });/*
34428  * Based on:
34429  * Ext JS Library 1.1.1
34430  * Copyright(c) 2006-2007, Ext JS, LLC.
34431  *
34432  * Originally Released Under LGPL - original licence link has changed is not relivant.
34433  *
34434  * Fork - LGPL
34435  * <script type="text/javascript">
34436  */
34437
34438 /**
34439 * @class Roo.tree.TreeFilter
34440 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34441 * @param {TreePanel} tree
34442 * @param {Object} config (optional)
34443  */
34444 Roo.tree.TreeFilter = function(tree, config){
34445     this.tree = tree;
34446     this.filtered = {};
34447     Roo.apply(this, config);
34448 };
34449
34450 Roo.tree.TreeFilter.prototype = {
34451     clearBlank:false,
34452     reverse:false,
34453     autoClear:false,
34454     remove:false,
34455
34456      /**
34457      * Filter the data by a specific attribute.
34458      * @param {String/RegExp} value Either string that the attribute value
34459      * should start with or a RegExp to test against the attribute
34460      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34461      * @param {TreeNode} startNode (optional) The node to start the filter at.
34462      */
34463     filter : function(value, attr, startNode){
34464         attr = attr || "text";
34465         var f;
34466         if(typeof value == "string"){
34467             var vlen = value.length;
34468             // auto clear empty filter
34469             if(vlen == 0 && this.clearBlank){
34470                 this.clear();
34471                 return;
34472             }
34473             value = value.toLowerCase();
34474             f = function(n){
34475                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34476             };
34477         }else if(value.exec){ // regex?
34478             f = function(n){
34479                 return value.test(n.attributes[attr]);
34480             };
34481         }else{
34482             throw 'Illegal filter type, must be string or regex';
34483         }
34484         this.filterBy(f, null, startNode);
34485         },
34486
34487     /**
34488      * Filter by a function. The passed function will be called with each
34489      * node in the tree (or from the startNode). If the function returns true, the node is kept
34490      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34491      * @param {Function} fn The filter function
34492      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34493      */
34494     filterBy : function(fn, scope, startNode){
34495         startNode = startNode || this.tree.root;
34496         if(this.autoClear){
34497             this.clear();
34498         }
34499         var af = this.filtered, rv = this.reverse;
34500         var f = function(n){
34501             if(n == startNode){
34502                 return true;
34503             }
34504             if(af[n.id]){
34505                 return false;
34506             }
34507             var m = fn.call(scope || n, n);
34508             if(!m || rv){
34509                 af[n.id] = n;
34510                 n.ui.hide();
34511                 return false;
34512             }
34513             return true;
34514         };
34515         startNode.cascade(f);
34516         if(this.remove){
34517            for(var id in af){
34518                if(typeof id != "function"){
34519                    var n = af[id];
34520                    if(n && n.parentNode){
34521                        n.parentNode.removeChild(n);
34522                    }
34523                }
34524            }
34525         }
34526     },
34527
34528     /**
34529      * Clears the current filter. Note: with the "remove" option
34530      * set a filter cannot be cleared.
34531      */
34532     clear : function(){
34533         var t = this.tree;
34534         var af = this.filtered;
34535         for(var id in af){
34536             if(typeof id != "function"){
34537                 var n = af[id];
34538                 if(n){
34539                     n.ui.show();
34540                 }
34541             }
34542         }
34543         this.filtered = {};
34544     }
34545 };
34546 /*
34547  * Based on:
34548  * Ext JS Library 1.1.1
34549  * Copyright(c) 2006-2007, Ext JS, LLC.
34550  *
34551  * Originally Released Under LGPL - original licence link has changed is not relivant.
34552  *
34553  * Fork - LGPL
34554  * <script type="text/javascript">
34555  */
34556  
34557
34558 /**
34559  * @class Roo.tree.TreeSorter
34560  * Provides sorting of nodes in a TreePanel
34561  * 
34562  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34563  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34564  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34565  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34566  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34567  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34568  * @constructor
34569  * @param {TreePanel} tree
34570  * @param {Object} config
34571  */
34572 Roo.tree.TreeSorter = function(tree, config){
34573     Roo.apply(this, config);
34574     tree.on("beforechildrenrendered", this.doSort, this);
34575     tree.on("append", this.updateSort, this);
34576     tree.on("insert", this.updateSort, this);
34577     
34578     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34579     var p = this.property || "text";
34580     var sortType = this.sortType;
34581     var fs = this.folderSort;
34582     var cs = this.caseSensitive === true;
34583     var leafAttr = this.leafAttr || 'leaf';
34584
34585     this.sortFn = function(n1, n2){
34586         if(fs){
34587             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34588                 return 1;
34589             }
34590             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34591                 return -1;
34592             }
34593         }
34594         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34595         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34596         if(v1 < v2){
34597                         return dsc ? +1 : -1;
34598                 }else if(v1 > v2){
34599                         return dsc ? -1 : +1;
34600         }else{
34601                 return 0;
34602         }
34603     };
34604 };
34605
34606 Roo.tree.TreeSorter.prototype = {
34607     doSort : function(node){
34608         node.sort(this.sortFn);
34609     },
34610     
34611     compareNodes : function(n1, n2){
34612         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34613     },
34614     
34615     updateSort : function(tree, node){
34616         if(node.childrenRendered){
34617             this.doSort.defer(1, this, [node]);
34618         }
34619     }
34620 };/*
34621  * Based on:
34622  * Ext JS Library 1.1.1
34623  * Copyright(c) 2006-2007, Ext JS, LLC.
34624  *
34625  * Originally Released Under LGPL - original licence link has changed is not relivant.
34626  *
34627  * Fork - LGPL
34628  * <script type="text/javascript">
34629  */
34630
34631 if(Roo.dd.DropZone){
34632     
34633 Roo.tree.TreeDropZone = function(tree, config){
34634     this.allowParentInsert = false;
34635     this.allowContainerDrop = false;
34636     this.appendOnly = false;
34637     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34638     this.tree = tree;
34639     this.lastInsertClass = "x-tree-no-status";
34640     this.dragOverData = {};
34641 };
34642
34643 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34644     ddGroup : "TreeDD",
34645     scroll:  true,
34646     
34647     expandDelay : 1000,
34648     
34649     expandNode : function(node){
34650         if(node.hasChildNodes() && !node.isExpanded()){
34651             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34652         }
34653     },
34654     
34655     queueExpand : function(node){
34656         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34657     },
34658     
34659     cancelExpand : function(){
34660         if(this.expandProcId){
34661             clearTimeout(this.expandProcId);
34662             this.expandProcId = false;
34663         }
34664     },
34665     
34666     isValidDropPoint : function(n, pt, dd, e, data){
34667         if(!n || !data){ return false; }
34668         var targetNode = n.node;
34669         var dropNode = data.node;
34670         // default drop rules
34671         if(!(targetNode && targetNode.isTarget && pt)){
34672             return false;
34673         }
34674         if(pt == "append" && targetNode.allowChildren === false){
34675             return false;
34676         }
34677         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34678             return false;
34679         }
34680         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34681             return false;
34682         }
34683         // reuse the object
34684         var overEvent = this.dragOverData;
34685         overEvent.tree = this.tree;
34686         overEvent.target = targetNode;
34687         overEvent.data = data;
34688         overEvent.point = pt;
34689         overEvent.source = dd;
34690         overEvent.rawEvent = e;
34691         overEvent.dropNode = dropNode;
34692         overEvent.cancel = false;  
34693         var result = this.tree.fireEvent("nodedragover", overEvent);
34694         return overEvent.cancel === false && result !== false;
34695     },
34696     
34697     getDropPoint : function(e, n, dd)
34698     {
34699         var tn = n.node;
34700         if(tn.isRoot){
34701             return tn.allowChildren !== false ? "append" : false; // always append for root
34702         }
34703         var dragEl = n.ddel;
34704         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34705         var y = Roo.lib.Event.getPageY(e);
34706         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34707         
34708         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34709         var noAppend = tn.allowChildren === false;
34710         if(this.appendOnly || tn.parentNode.allowChildren === false){
34711             return noAppend ? false : "append";
34712         }
34713         var noBelow = false;
34714         if(!this.allowParentInsert){
34715             noBelow = tn.hasChildNodes() && tn.isExpanded();
34716         }
34717         var q = (b - t) / (noAppend ? 2 : 3);
34718         if(y >= t && y < (t + q)){
34719             return "above";
34720         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34721             return "below";
34722         }else{
34723             return "append";
34724         }
34725     },
34726     
34727     onNodeEnter : function(n, dd, e, data)
34728     {
34729         this.cancelExpand();
34730     },
34731     
34732     onNodeOver : function(n, dd, e, data)
34733     {
34734        
34735         var pt = this.getDropPoint(e, n, dd);
34736         var node = n.node;
34737         
34738         // auto node expand check
34739         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34740             this.queueExpand(node);
34741         }else if(pt != "append"){
34742             this.cancelExpand();
34743         }
34744         
34745         // set the insert point style on the target node
34746         var returnCls = this.dropNotAllowed;
34747         if(this.isValidDropPoint(n, pt, dd, e, data)){
34748            if(pt){
34749                var el = n.ddel;
34750                var cls;
34751                if(pt == "above"){
34752                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34753                    cls = "x-tree-drag-insert-above";
34754                }else if(pt == "below"){
34755                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34756                    cls = "x-tree-drag-insert-below";
34757                }else{
34758                    returnCls = "x-tree-drop-ok-append";
34759                    cls = "x-tree-drag-append";
34760                }
34761                if(this.lastInsertClass != cls){
34762                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34763                    this.lastInsertClass = cls;
34764                }
34765            }
34766        }
34767        return returnCls;
34768     },
34769     
34770     onNodeOut : function(n, dd, e, data){
34771         
34772         this.cancelExpand();
34773         this.removeDropIndicators(n);
34774     },
34775     
34776     onNodeDrop : function(n, dd, e, data){
34777         var point = this.getDropPoint(e, n, dd);
34778         var targetNode = n.node;
34779         targetNode.ui.startDrop();
34780         if(!this.isValidDropPoint(n, point, dd, e, data)){
34781             targetNode.ui.endDrop();
34782             return false;
34783         }
34784         // first try to find the drop node
34785         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34786         var dropEvent = {
34787             tree : this.tree,
34788             target: targetNode,
34789             data: data,
34790             point: point,
34791             source: dd,
34792             rawEvent: e,
34793             dropNode: dropNode,
34794             cancel: !dropNode   
34795         };
34796         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34797         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34798             targetNode.ui.endDrop();
34799             return false;
34800         }
34801         // allow target changing
34802         targetNode = dropEvent.target;
34803         if(point == "append" && !targetNode.isExpanded()){
34804             targetNode.expand(false, null, function(){
34805                 this.completeDrop(dropEvent);
34806             }.createDelegate(this));
34807         }else{
34808             this.completeDrop(dropEvent);
34809         }
34810         return true;
34811     },
34812     
34813     completeDrop : function(de){
34814         var ns = de.dropNode, p = de.point, t = de.target;
34815         if(!(ns instanceof Array)){
34816             ns = [ns];
34817         }
34818         var n;
34819         for(var i = 0, len = ns.length; i < len; i++){
34820             n = ns[i];
34821             if(p == "above"){
34822                 t.parentNode.insertBefore(n, t);
34823             }else if(p == "below"){
34824                 t.parentNode.insertBefore(n, t.nextSibling);
34825             }else{
34826                 t.appendChild(n);
34827             }
34828         }
34829         n.ui.focus();
34830         if(this.tree.hlDrop){
34831             n.ui.highlight();
34832         }
34833         t.ui.endDrop();
34834         this.tree.fireEvent("nodedrop", de);
34835     },
34836     
34837     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34838         if(this.tree.hlDrop){
34839             dropNode.ui.focus();
34840             dropNode.ui.highlight();
34841         }
34842         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34843     },
34844     
34845     getTree : function(){
34846         return this.tree;
34847     },
34848     
34849     removeDropIndicators : function(n){
34850         if(n && n.ddel){
34851             var el = n.ddel;
34852             Roo.fly(el).removeClass([
34853                     "x-tree-drag-insert-above",
34854                     "x-tree-drag-insert-below",
34855                     "x-tree-drag-append"]);
34856             this.lastInsertClass = "_noclass";
34857         }
34858     },
34859     
34860     beforeDragDrop : function(target, e, id){
34861         this.cancelExpand();
34862         return true;
34863     },
34864     
34865     afterRepair : function(data){
34866         if(data && Roo.enableFx){
34867             data.node.ui.highlight();
34868         }
34869         this.hideProxy();
34870     } 
34871     
34872 });
34873
34874 }
34875 /*
34876  * Based on:
34877  * Ext JS Library 1.1.1
34878  * Copyright(c) 2006-2007, Ext JS, LLC.
34879  *
34880  * Originally Released Under LGPL - original licence link has changed is not relivant.
34881  *
34882  * Fork - LGPL
34883  * <script type="text/javascript">
34884  */
34885  
34886
34887 if(Roo.dd.DragZone){
34888 Roo.tree.TreeDragZone = function(tree, config){
34889     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34890     this.tree = tree;
34891 };
34892
34893 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34894     ddGroup : "TreeDD",
34895    
34896     onBeforeDrag : function(data, e){
34897         var n = data.node;
34898         return n && n.draggable && !n.disabled;
34899     },
34900      
34901     
34902     onInitDrag : function(e){
34903         var data = this.dragData;
34904         this.tree.getSelectionModel().select(data.node);
34905         this.proxy.update("");
34906         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34907         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34908     },
34909     
34910     getRepairXY : function(e, data){
34911         return data.node.ui.getDDRepairXY();
34912     },
34913     
34914     onEndDrag : function(data, e){
34915         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34916         
34917         
34918     },
34919     
34920     onValidDrop : function(dd, e, id){
34921         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34922         this.hideProxy();
34923     },
34924     
34925     beforeInvalidDrop : function(e, id){
34926         // this scrolls the original position back into view
34927         var sm = this.tree.getSelectionModel();
34928         sm.clearSelections();
34929         sm.select(this.dragData.node);
34930     }
34931 });
34932 }/*
34933  * Based on:
34934  * Ext JS Library 1.1.1
34935  * Copyright(c) 2006-2007, Ext JS, LLC.
34936  *
34937  * Originally Released Under LGPL - original licence link has changed is not relivant.
34938  *
34939  * Fork - LGPL
34940  * <script type="text/javascript">
34941  */
34942 /**
34943  * @class Roo.tree.TreeEditor
34944  * @extends Roo.Editor
34945  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34946  * as the editor field.
34947  * @constructor
34948  * @param {Object} config (used to be the tree panel.)
34949  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34950  * 
34951  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34952  * @cfg {Roo.form.TextField|Object} field The field configuration
34953  *
34954  * 
34955  */
34956 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34957     var tree = config;
34958     var field;
34959     if (oldconfig) { // old style..
34960         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34961     } else {
34962         // new style..
34963         tree = config.tree;
34964         config.field = config.field  || {};
34965         config.field.xtype = 'TextField';
34966         field = Roo.factory(config.field, Roo.form);
34967     }
34968     config = config || {};
34969     
34970     
34971     this.addEvents({
34972         /**
34973          * @event beforenodeedit
34974          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34975          * false from the handler of this event.
34976          * @param {Editor} this
34977          * @param {Roo.tree.Node} node 
34978          */
34979         "beforenodeedit" : true
34980     });
34981     
34982     //Roo.log(config);
34983     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34984
34985     this.tree = tree;
34986
34987     tree.on('beforeclick', this.beforeNodeClick, this);
34988     tree.getTreeEl().on('mousedown', this.hide, this);
34989     this.on('complete', this.updateNode, this);
34990     this.on('beforestartedit', this.fitToTree, this);
34991     this.on('startedit', this.bindScroll, this, {delay:10});
34992     this.on('specialkey', this.onSpecialKey, this);
34993 };
34994
34995 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34996     /**
34997      * @cfg {String} alignment
34998      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34999      */
35000     alignment: "l-l",
35001     // inherit
35002     autoSize: false,
35003     /**
35004      * @cfg {Boolean} hideEl
35005      * True to hide the bound element while the editor is displayed (defaults to false)
35006      */
35007     hideEl : false,
35008     /**
35009      * @cfg {String} cls
35010      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35011      */
35012     cls: "x-small-editor x-tree-editor",
35013     /**
35014      * @cfg {Boolean} shim
35015      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35016      */
35017     shim:false,
35018     // inherit
35019     shadow:"frame",
35020     /**
35021      * @cfg {Number} maxWidth
35022      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35023      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35024      * scroll and client offsets into account prior to each edit.
35025      */
35026     maxWidth: 250,
35027
35028     editDelay : 350,
35029
35030     // private
35031     fitToTree : function(ed, el){
35032         var td = this.tree.getTreeEl().dom, nd = el.dom;
35033         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35034             td.scrollLeft = nd.offsetLeft;
35035         }
35036         var w = Math.min(
35037                 this.maxWidth,
35038                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35039         this.setSize(w, '');
35040         
35041         return this.fireEvent('beforenodeedit', this, this.editNode);
35042         
35043     },
35044
35045     // private
35046     triggerEdit : function(node){
35047         this.completeEdit();
35048         this.editNode = node;
35049         this.startEdit(node.ui.textNode, node.text);
35050     },
35051
35052     // private
35053     bindScroll : function(){
35054         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35055     },
35056
35057     // private
35058     beforeNodeClick : function(node, e){
35059         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35060         this.lastClick = new Date();
35061         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35062             e.stopEvent();
35063             this.triggerEdit(node);
35064             return false;
35065         }
35066         return true;
35067     },
35068
35069     // private
35070     updateNode : function(ed, value){
35071         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35072         this.editNode.setText(value);
35073     },
35074
35075     // private
35076     onHide : function(){
35077         Roo.tree.TreeEditor.superclass.onHide.call(this);
35078         if(this.editNode){
35079             this.editNode.ui.focus();
35080         }
35081     },
35082
35083     // private
35084     onSpecialKey : function(field, e){
35085         var k = e.getKey();
35086         if(k == e.ESC){
35087             e.stopEvent();
35088             this.cancelEdit();
35089         }else if(k == e.ENTER && !e.hasModifier()){
35090             e.stopEvent();
35091             this.completeEdit();
35092         }
35093     }
35094 });//<Script type="text/javascript">
35095 /*
35096  * Based on:
35097  * Ext JS Library 1.1.1
35098  * Copyright(c) 2006-2007, Ext JS, LLC.
35099  *
35100  * Originally Released Under LGPL - original licence link has changed is not relivant.
35101  *
35102  * Fork - LGPL
35103  * <script type="text/javascript">
35104  */
35105  
35106 /**
35107  * Not documented??? - probably should be...
35108  */
35109
35110 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35111     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35112     
35113     renderElements : function(n, a, targetNode, bulkRender){
35114         //consel.log("renderElements?");
35115         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35116
35117         var t = n.getOwnerTree();
35118         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35119         
35120         var cols = t.columns;
35121         var bw = t.borderWidth;
35122         var c = cols[0];
35123         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35124          var cb = typeof a.checked == "boolean";
35125         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35126         var colcls = 'x-t-' + tid + '-c0';
35127         var buf = [
35128             '<li class="x-tree-node">',
35129             
35130                 
35131                 '<div class="x-tree-node-el ', a.cls,'">',
35132                     // extran...
35133                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35134                 
35135                 
35136                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35137                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35138                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35139                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35140                            (a.iconCls ? ' '+a.iconCls : ''),
35141                            '" unselectable="on" />',
35142                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35143                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35144                              
35145                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35146                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35147                             '<span unselectable="on" qtip="' + tx + '">',
35148                              tx,
35149                              '</span></a>' ,
35150                     '</div>',
35151                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35152                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35153                  ];
35154         for(var i = 1, len = cols.length; i < len; i++){
35155             c = cols[i];
35156             colcls = 'x-t-' + tid + '-c' +i;
35157             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35158             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35159                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35160                       "</div>");
35161          }
35162          
35163          buf.push(
35164             '</a>',
35165             '<div class="x-clear"></div></div>',
35166             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35167             "</li>");
35168         
35169         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35170             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35171                                 n.nextSibling.ui.getEl(), buf.join(""));
35172         }else{
35173             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35174         }
35175         var el = this.wrap.firstChild;
35176         this.elRow = el;
35177         this.elNode = el.firstChild;
35178         this.ranchor = el.childNodes[1];
35179         this.ctNode = this.wrap.childNodes[1];
35180         var cs = el.firstChild.childNodes;
35181         this.indentNode = cs[0];
35182         this.ecNode = cs[1];
35183         this.iconNode = cs[2];
35184         var index = 3;
35185         if(cb){
35186             this.checkbox = cs[3];
35187             index++;
35188         }
35189         this.anchor = cs[index];
35190         
35191         this.textNode = cs[index].firstChild;
35192         
35193         //el.on("click", this.onClick, this);
35194         //el.on("dblclick", this.onDblClick, this);
35195         
35196         
35197        // console.log(this);
35198     },
35199     initEvents : function(){
35200         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35201         
35202             
35203         var a = this.ranchor;
35204
35205         var el = Roo.get(a);
35206
35207         if(Roo.isOpera){ // opera render bug ignores the CSS
35208             el.setStyle("text-decoration", "none");
35209         }
35210
35211         el.on("click", this.onClick, this);
35212         el.on("dblclick", this.onDblClick, this);
35213         el.on("contextmenu", this.onContextMenu, this);
35214         
35215     },
35216     
35217     /*onSelectedChange : function(state){
35218         if(state){
35219             this.focus();
35220             this.addClass("x-tree-selected");
35221         }else{
35222             //this.blur();
35223             this.removeClass("x-tree-selected");
35224         }
35225     },*/
35226     addClass : function(cls){
35227         if(this.elRow){
35228             Roo.fly(this.elRow).addClass(cls);
35229         }
35230         
35231     },
35232     
35233     
35234     removeClass : function(cls){
35235         if(this.elRow){
35236             Roo.fly(this.elRow).removeClass(cls);
35237         }
35238     }
35239
35240     
35241     
35242 });//<Script type="text/javascript">
35243
35244 /*
35245  * Based on:
35246  * Ext JS Library 1.1.1
35247  * Copyright(c) 2006-2007, Ext JS, LLC.
35248  *
35249  * Originally Released Under LGPL - original licence link has changed is not relivant.
35250  *
35251  * Fork - LGPL
35252  * <script type="text/javascript">
35253  */
35254  
35255
35256 /**
35257  * @class Roo.tree.ColumnTree
35258  * @extends Roo.data.TreePanel
35259  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35260  * @cfg {int} borderWidth  compined right/left border allowance
35261  * @constructor
35262  * @param {String/HTMLElement/Element} el The container element
35263  * @param {Object} config
35264  */
35265 Roo.tree.ColumnTree =  function(el, config)
35266 {
35267    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35268    this.addEvents({
35269         /**
35270         * @event resize
35271         * Fire this event on a container when it resizes
35272         * @param {int} w Width
35273         * @param {int} h Height
35274         */
35275        "resize" : true
35276     });
35277     this.on('resize', this.onResize, this);
35278 };
35279
35280 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35281     //lines:false,
35282     
35283     
35284     borderWidth: Roo.isBorderBox ? 0 : 2, 
35285     headEls : false,
35286     
35287     render : function(){
35288         // add the header.....
35289        
35290         Roo.tree.ColumnTree.superclass.render.apply(this);
35291         
35292         this.el.addClass('x-column-tree');
35293         
35294         this.headers = this.el.createChild(
35295             {cls:'x-tree-headers'},this.innerCt.dom);
35296    
35297         var cols = this.columns, c;
35298         var totalWidth = 0;
35299         this.headEls = [];
35300         var  len = cols.length;
35301         for(var i = 0; i < len; i++){
35302              c = cols[i];
35303              totalWidth += c.width;
35304             this.headEls.push(this.headers.createChild({
35305                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35306                  cn: {
35307                      cls:'x-tree-hd-text',
35308                      html: c.header
35309                  },
35310                  style:'width:'+(c.width-this.borderWidth)+'px;'
35311              }));
35312         }
35313         this.headers.createChild({cls:'x-clear'});
35314         // prevent floats from wrapping when clipped
35315         this.headers.setWidth(totalWidth);
35316         //this.innerCt.setWidth(totalWidth);
35317         this.innerCt.setStyle({ overflow: 'auto' });
35318         this.onResize(this.width, this.height);
35319              
35320         
35321     },
35322     onResize : function(w,h)
35323     {
35324         this.height = h;
35325         this.width = w;
35326         // resize cols..
35327         this.innerCt.setWidth(this.width);
35328         this.innerCt.setHeight(this.height-20);
35329         
35330         // headers...
35331         var cols = this.columns, c;
35332         var totalWidth = 0;
35333         var expEl = false;
35334         var len = cols.length;
35335         for(var i = 0; i < len; i++){
35336             c = cols[i];
35337             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35338                 // it's the expander..
35339                 expEl  = this.headEls[i];
35340                 continue;
35341             }
35342             totalWidth += c.width;
35343             
35344         }
35345         if (expEl) {
35346             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35347         }
35348         this.headers.setWidth(w-20);
35349
35350         
35351         
35352         
35353     }
35354 });
35355 /*
35356  * Based on:
35357  * Ext JS Library 1.1.1
35358  * Copyright(c) 2006-2007, Ext JS, LLC.
35359  *
35360  * Originally Released Under LGPL - original licence link has changed is not relivant.
35361  *
35362  * Fork - LGPL
35363  * <script type="text/javascript">
35364  */
35365  
35366 /**
35367  * @class Roo.menu.Menu
35368  * @extends Roo.util.Observable
35369  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35370  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35371  * @constructor
35372  * Creates a new Menu
35373  * @param {Object} config Configuration options
35374  */
35375 Roo.menu.Menu = function(config){
35376     Roo.apply(this, config);
35377     this.id = this.id || Roo.id();
35378     this.addEvents({
35379         /**
35380          * @event beforeshow
35381          * Fires before this menu is displayed
35382          * @param {Roo.menu.Menu} this
35383          */
35384         beforeshow : true,
35385         /**
35386          * @event beforehide
35387          * Fires before this menu is hidden
35388          * @param {Roo.menu.Menu} this
35389          */
35390         beforehide : true,
35391         /**
35392          * @event show
35393          * Fires after this menu is displayed
35394          * @param {Roo.menu.Menu} this
35395          */
35396         show : true,
35397         /**
35398          * @event hide
35399          * Fires after this menu is hidden
35400          * @param {Roo.menu.Menu} this
35401          */
35402         hide : true,
35403         /**
35404          * @event click
35405          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35406          * @param {Roo.menu.Menu} this
35407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35408          * @param {Roo.EventObject} e
35409          */
35410         click : true,
35411         /**
35412          * @event mouseover
35413          * Fires when the mouse is hovering over this menu
35414          * @param {Roo.menu.Menu} this
35415          * @param {Roo.EventObject} e
35416          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35417          */
35418         mouseover : true,
35419         /**
35420          * @event mouseout
35421          * Fires when the mouse exits this menu
35422          * @param {Roo.menu.Menu} this
35423          * @param {Roo.EventObject} e
35424          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35425          */
35426         mouseout : true,
35427         /**
35428          * @event itemclick
35429          * Fires when a menu item contained in this menu is clicked
35430          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35431          * @param {Roo.EventObject} e
35432          */
35433         itemclick: true
35434     });
35435     if (this.registerMenu) {
35436         Roo.menu.MenuMgr.register(this);
35437     }
35438     
35439     var mis = this.items;
35440     this.items = new Roo.util.MixedCollection();
35441     if(mis){
35442         this.add.apply(this, mis);
35443     }
35444 };
35445
35446 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35447     /**
35448      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35449      */
35450     minWidth : 120,
35451     /**
35452      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35453      * for bottom-right shadow (defaults to "sides")
35454      */
35455     shadow : "sides",
35456     /**
35457      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35458      * this menu (defaults to "tl-tr?")
35459      */
35460     subMenuAlign : "tl-tr?",
35461     /**
35462      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35463      * relative to its element of origin (defaults to "tl-bl?")
35464      */
35465     defaultAlign : "tl-bl?",
35466     /**
35467      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35468      */
35469     allowOtherMenus : false,
35470     /**
35471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35472      */
35473     registerMenu : true,
35474
35475     hidden:true,
35476
35477     // private
35478     render : function(){
35479         if(this.el){
35480             return;
35481         }
35482         var el = this.el = new Roo.Layer({
35483             cls: "x-menu",
35484             shadow:this.shadow,
35485             constrain: false,
35486             parentEl: this.parentEl || document.body,
35487             zindex:15000
35488         });
35489
35490         this.keyNav = new Roo.menu.MenuNav(this);
35491
35492         if(this.plain){
35493             el.addClass("x-menu-plain");
35494         }
35495         if(this.cls){
35496             el.addClass(this.cls);
35497         }
35498         // generic focus element
35499         this.focusEl = el.createChild({
35500             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35501         });
35502         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35503         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35504         
35505         ul.on("mouseover", this.onMouseOver, this);
35506         ul.on("mouseout", this.onMouseOut, this);
35507         this.items.each(function(item){
35508             if (item.hidden) {
35509                 return;
35510             }
35511             
35512             var li = document.createElement("li");
35513             li.className = "x-menu-list-item";
35514             ul.dom.appendChild(li);
35515             item.render(li, this);
35516         }, this);
35517         this.ul = ul;
35518         this.autoWidth();
35519     },
35520
35521     // private
35522     autoWidth : function(){
35523         var el = this.el, ul = this.ul;
35524         if(!el){
35525             return;
35526         }
35527         var w = this.width;
35528         if(w){
35529             el.setWidth(w);
35530         }else if(Roo.isIE){
35531             el.setWidth(this.minWidth);
35532             var t = el.dom.offsetWidth; // force recalc
35533             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35534         }
35535     },
35536
35537     // private
35538     delayAutoWidth : function(){
35539         if(this.rendered){
35540             if(!this.awTask){
35541                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35542             }
35543             this.awTask.delay(20);
35544         }
35545     },
35546
35547     // private
35548     findTargetItem : function(e){
35549         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35550         if(t && t.menuItemId){
35551             return this.items.get(t.menuItemId);
35552         }
35553     },
35554
35555     // private
35556     onClick : function(e){
35557         Roo.log("menu.onClick");
35558         var t = this.findTargetItem(e);
35559         if(!t){
35560             return;
35561         }
35562         Roo.log(e);
35563         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35564             if(t == this.activeItem && t.shouldDeactivate(e)){
35565                 this.activeItem.deactivate();
35566                 delete this.activeItem;
35567                 return;
35568             }
35569             if(t.canActivate){
35570                 this.setActiveItem(t, true);
35571             }
35572             return;
35573             
35574             
35575         }
35576         
35577         t.onClick(e);
35578         this.fireEvent("click", this, t, e);
35579     },
35580
35581     // private
35582     setActiveItem : function(item, autoExpand){
35583         if(item != this.activeItem){
35584             if(this.activeItem){
35585                 this.activeItem.deactivate();
35586             }
35587             this.activeItem = item;
35588             item.activate(autoExpand);
35589         }else if(autoExpand){
35590             item.expandMenu();
35591         }
35592     },
35593
35594     // private
35595     tryActivate : function(start, step){
35596         var items = this.items;
35597         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35598             var item = items.get(i);
35599             if(!item.disabled && item.canActivate){
35600                 this.setActiveItem(item, false);
35601                 return item;
35602             }
35603         }
35604         return false;
35605     },
35606
35607     // private
35608     onMouseOver : function(e){
35609         var t;
35610         if(t = this.findTargetItem(e)){
35611             if(t.canActivate && !t.disabled){
35612                 this.setActiveItem(t, true);
35613             }
35614         }
35615         this.fireEvent("mouseover", this, e, t);
35616     },
35617
35618     // private
35619     onMouseOut : function(e){
35620         var t;
35621         if(t = this.findTargetItem(e)){
35622             if(t == this.activeItem && t.shouldDeactivate(e)){
35623                 this.activeItem.deactivate();
35624                 delete this.activeItem;
35625             }
35626         }
35627         this.fireEvent("mouseout", this, e, t);
35628     },
35629
35630     /**
35631      * Read-only.  Returns true if the menu is currently displayed, else false.
35632      * @type Boolean
35633      */
35634     isVisible : function(){
35635         return this.el && !this.hidden;
35636     },
35637
35638     /**
35639      * Displays this menu relative to another element
35640      * @param {String/HTMLElement/Roo.Element} element The element to align to
35641      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35642      * the element (defaults to this.defaultAlign)
35643      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35644      */
35645     show : function(el, pos, parentMenu){
35646         this.parentMenu = parentMenu;
35647         if(!this.el){
35648             this.render();
35649         }
35650         this.fireEvent("beforeshow", this);
35651         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35652     },
35653
35654     /**
35655      * Displays this menu at a specific xy position
35656      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35657      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35658      */
35659     showAt : function(xy, parentMenu, /* private: */_e){
35660         this.parentMenu = parentMenu;
35661         if(!this.el){
35662             this.render();
35663         }
35664         if(_e !== false){
35665             this.fireEvent("beforeshow", this);
35666             xy = this.el.adjustForConstraints(xy);
35667         }
35668         this.el.setXY(xy);
35669         this.el.show();
35670         this.hidden = false;
35671         this.focus();
35672         this.fireEvent("show", this);
35673     },
35674
35675     focus : function(){
35676         if(!this.hidden){
35677             this.doFocus.defer(50, this);
35678         }
35679     },
35680
35681     doFocus : function(){
35682         if(!this.hidden){
35683             this.focusEl.focus();
35684         }
35685     },
35686
35687     /**
35688      * Hides this menu and optionally all parent menus
35689      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35690      */
35691     hide : function(deep){
35692         if(this.el && this.isVisible()){
35693             this.fireEvent("beforehide", this);
35694             if(this.activeItem){
35695                 this.activeItem.deactivate();
35696                 this.activeItem = null;
35697             }
35698             this.el.hide();
35699             this.hidden = true;
35700             this.fireEvent("hide", this);
35701         }
35702         if(deep === true && this.parentMenu){
35703             this.parentMenu.hide(true);
35704         }
35705     },
35706
35707     /**
35708      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35709      * Any of the following are valid:
35710      * <ul>
35711      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35712      * <li>An HTMLElement object which will be converted to a menu item</li>
35713      * <li>A menu item config object that will be created as a new menu item</li>
35714      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35715      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35716      * </ul>
35717      * Usage:
35718      * <pre><code>
35719 // Create the menu
35720 var menu = new Roo.menu.Menu();
35721
35722 // Create a menu item to add by reference
35723 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35724
35725 // Add a bunch of items at once using different methods.
35726 // Only the last item added will be returned.
35727 var item = menu.add(
35728     menuItem,                // add existing item by ref
35729     'Dynamic Item',          // new TextItem
35730     '-',                     // new separator
35731     { text: 'Config Item' }  // new item by config
35732 );
35733 </code></pre>
35734      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35735      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35736      */
35737     add : function(){
35738         var a = arguments, l = a.length, item;
35739         for(var i = 0; i < l; i++){
35740             var el = a[i];
35741             if ((typeof(el) == "object") && el.xtype && el.xns) {
35742                 el = Roo.factory(el, Roo.menu);
35743             }
35744             
35745             if(el.render){ // some kind of Item
35746                 item = this.addItem(el);
35747             }else if(typeof el == "string"){ // string
35748                 if(el == "separator" || el == "-"){
35749                     item = this.addSeparator();
35750                 }else{
35751                     item = this.addText(el);
35752                 }
35753             }else if(el.tagName || el.el){ // element
35754                 item = this.addElement(el);
35755             }else if(typeof el == "object"){ // must be menu item config?
35756                 item = this.addMenuItem(el);
35757             }
35758         }
35759         return item;
35760     },
35761
35762     /**
35763      * Returns this menu's underlying {@link Roo.Element} object
35764      * @return {Roo.Element} The element
35765      */
35766     getEl : function(){
35767         if(!this.el){
35768             this.render();
35769         }
35770         return this.el;
35771     },
35772
35773     /**
35774      * Adds a separator bar to the menu
35775      * @return {Roo.menu.Item} The menu item that was added
35776      */
35777     addSeparator : function(){
35778         return this.addItem(new Roo.menu.Separator());
35779     },
35780
35781     /**
35782      * Adds an {@link Roo.Element} object to the menu
35783      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35784      * @return {Roo.menu.Item} The menu item that was added
35785      */
35786     addElement : function(el){
35787         return this.addItem(new Roo.menu.BaseItem(el));
35788     },
35789
35790     /**
35791      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35792      * @param {Roo.menu.Item} item The menu item to add
35793      * @return {Roo.menu.Item} The menu item that was added
35794      */
35795     addItem : function(item){
35796         this.items.add(item);
35797         if(this.ul){
35798             var li = document.createElement("li");
35799             li.className = "x-menu-list-item";
35800             this.ul.dom.appendChild(li);
35801             item.render(li, this);
35802             this.delayAutoWidth();
35803         }
35804         return item;
35805     },
35806
35807     /**
35808      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35809      * @param {Object} config A MenuItem config object
35810      * @return {Roo.menu.Item} The menu item that was added
35811      */
35812     addMenuItem : function(config){
35813         if(!(config instanceof Roo.menu.Item)){
35814             if(typeof config.checked == "boolean"){ // must be check menu item config?
35815                 config = new Roo.menu.CheckItem(config);
35816             }else{
35817                 config = new Roo.menu.Item(config);
35818             }
35819         }
35820         return this.addItem(config);
35821     },
35822
35823     /**
35824      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35825      * @param {String} text The text to display in the menu item
35826      * @return {Roo.menu.Item} The menu item that was added
35827      */
35828     addText : function(text){
35829         return this.addItem(new Roo.menu.TextItem({ text : text }));
35830     },
35831
35832     /**
35833      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35834      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35835      * @param {Roo.menu.Item} item The menu item to add
35836      * @return {Roo.menu.Item} The menu item that was added
35837      */
35838     insert : function(index, item){
35839         this.items.insert(index, item);
35840         if(this.ul){
35841             var li = document.createElement("li");
35842             li.className = "x-menu-list-item";
35843             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35844             item.render(li, this);
35845             this.delayAutoWidth();
35846         }
35847         return item;
35848     },
35849
35850     /**
35851      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35852      * @param {Roo.menu.Item} item The menu item to remove
35853      */
35854     remove : function(item){
35855         this.items.removeKey(item.id);
35856         item.destroy();
35857     },
35858
35859     /**
35860      * Removes and destroys all items in the menu
35861      */
35862     removeAll : function(){
35863         var f;
35864         while(f = this.items.first()){
35865             this.remove(f);
35866         }
35867     }
35868 });
35869
35870 // MenuNav is a private utility class used internally by the Menu
35871 Roo.menu.MenuNav = function(menu){
35872     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35873     this.scope = this.menu = menu;
35874 };
35875
35876 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35877     doRelay : function(e, h){
35878         var k = e.getKey();
35879         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35880             this.menu.tryActivate(0, 1);
35881             return false;
35882         }
35883         return h.call(this.scope || this, e, this.menu);
35884     },
35885
35886     up : function(e, m){
35887         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35888             m.tryActivate(m.items.length-1, -1);
35889         }
35890     },
35891
35892     down : function(e, m){
35893         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35894             m.tryActivate(0, 1);
35895         }
35896     },
35897
35898     right : function(e, m){
35899         if(m.activeItem){
35900             m.activeItem.expandMenu(true);
35901         }
35902     },
35903
35904     left : function(e, m){
35905         m.hide();
35906         if(m.parentMenu && m.parentMenu.activeItem){
35907             m.parentMenu.activeItem.activate();
35908         }
35909     },
35910
35911     enter : function(e, m){
35912         if(m.activeItem){
35913             e.stopPropagation();
35914             m.activeItem.onClick(e);
35915             m.fireEvent("click", this, m.activeItem);
35916             return true;
35917         }
35918     }
35919 });/*
35920  * Based on:
35921  * Ext JS Library 1.1.1
35922  * Copyright(c) 2006-2007, Ext JS, LLC.
35923  *
35924  * Originally Released Under LGPL - original licence link has changed is not relivant.
35925  *
35926  * Fork - LGPL
35927  * <script type="text/javascript">
35928  */
35929  
35930 /**
35931  * @class Roo.menu.MenuMgr
35932  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35933  * @singleton
35934  */
35935 Roo.menu.MenuMgr = function(){
35936    var menus, active, groups = {}, attached = false, lastShow = new Date();
35937
35938    // private - called when first menu is created
35939    function init(){
35940        menus = {};
35941        active = new Roo.util.MixedCollection();
35942        Roo.get(document).addKeyListener(27, function(){
35943            if(active.length > 0){
35944                hideAll();
35945            }
35946        });
35947    }
35948
35949    // private
35950    function hideAll(){
35951        if(active && active.length > 0){
35952            var c = active.clone();
35953            c.each(function(m){
35954                m.hide();
35955            });
35956        }
35957    }
35958
35959    // private
35960    function onHide(m){
35961        active.remove(m);
35962        if(active.length < 1){
35963            Roo.get(document).un("mousedown", onMouseDown);
35964            attached = false;
35965        }
35966    }
35967
35968    // private
35969    function onShow(m){
35970        var last = active.last();
35971        lastShow = new Date();
35972        active.add(m);
35973        if(!attached){
35974            Roo.get(document).on("mousedown", onMouseDown);
35975            attached = true;
35976        }
35977        if(m.parentMenu){
35978           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35979           m.parentMenu.activeChild = m;
35980        }else if(last && last.isVisible()){
35981           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35982        }
35983    }
35984
35985    // private
35986    function onBeforeHide(m){
35987        if(m.activeChild){
35988            m.activeChild.hide();
35989        }
35990        if(m.autoHideTimer){
35991            clearTimeout(m.autoHideTimer);
35992            delete m.autoHideTimer;
35993        }
35994    }
35995
35996    // private
35997    function onBeforeShow(m){
35998        var pm = m.parentMenu;
35999        if(!pm && !m.allowOtherMenus){
36000            hideAll();
36001        }else if(pm && pm.activeChild && active != m){
36002            pm.activeChild.hide();
36003        }
36004    }
36005
36006    // private
36007    function onMouseDown(e){
36008        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36009            hideAll();
36010        }
36011    }
36012
36013    // private
36014    function onBeforeCheck(mi, state){
36015        if(state){
36016            var g = groups[mi.group];
36017            for(var i = 0, l = g.length; i < l; i++){
36018                if(g[i] != mi){
36019                    g[i].setChecked(false);
36020                }
36021            }
36022        }
36023    }
36024
36025    return {
36026
36027        /**
36028         * Hides all menus that are currently visible
36029         */
36030        hideAll : function(){
36031             hideAll();  
36032        },
36033
36034        // private
36035        register : function(menu){
36036            if(!menus){
36037                init();
36038            }
36039            menus[menu.id] = menu;
36040            menu.on("beforehide", onBeforeHide);
36041            menu.on("hide", onHide);
36042            menu.on("beforeshow", onBeforeShow);
36043            menu.on("show", onShow);
36044            var g = menu.group;
36045            if(g && menu.events["checkchange"]){
36046                if(!groups[g]){
36047                    groups[g] = [];
36048                }
36049                groups[g].push(menu);
36050                menu.on("checkchange", onCheck);
36051            }
36052        },
36053
36054         /**
36055          * Returns a {@link Roo.menu.Menu} object
36056          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36057          * be used to generate and return a new Menu instance.
36058          */
36059        get : function(menu){
36060            if(typeof menu == "string"){ // menu id
36061                return menus[menu];
36062            }else if(menu.events){  // menu instance
36063                return menu;
36064            }else if(typeof menu.length == 'number'){ // array of menu items?
36065                return new Roo.menu.Menu({items:menu});
36066            }else{ // otherwise, must be a config
36067                return new Roo.menu.Menu(menu);
36068            }
36069        },
36070
36071        // private
36072        unregister : function(menu){
36073            delete menus[menu.id];
36074            menu.un("beforehide", onBeforeHide);
36075            menu.un("hide", onHide);
36076            menu.un("beforeshow", onBeforeShow);
36077            menu.un("show", onShow);
36078            var g = menu.group;
36079            if(g && menu.events["checkchange"]){
36080                groups[g].remove(menu);
36081                menu.un("checkchange", onCheck);
36082            }
36083        },
36084
36085        // private
36086        registerCheckable : function(menuItem){
36087            var g = menuItem.group;
36088            if(g){
36089                if(!groups[g]){
36090                    groups[g] = [];
36091                }
36092                groups[g].push(menuItem);
36093                menuItem.on("beforecheckchange", onBeforeCheck);
36094            }
36095        },
36096
36097        // private
36098        unregisterCheckable : function(menuItem){
36099            var g = menuItem.group;
36100            if(g){
36101                groups[g].remove(menuItem);
36102                menuItem.un("beforecheckchange", onBeforeCheck);
36103            }
36104        }
36105    };
36106 }();/*
36107  * Based on:
36108  * Ext JS Library 1.1.1
36109  * Copyright(c) 2006-2007, Ext JS, LLC.
36110  *
36111  * Originally Released Under LGPL - original licence link has changed is not relivant.
36112  *
36113  * Fork - LGPL
36114  * <script type="text/javascript">
36115  */
36116  
36117
36118 /**
36119  * @class Roo.menu.BaseItem
36120  * @extends Roo.Component
36121  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36122  * management and base configuration options shared by all menu components.
36123  * @constructor
36124  * Creates a new BaseItem
36125  * @param {Object} config Configuration options
36126  */
36127 Roo.menu.BaseItem = function(config){
36128     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36129
36130     this.addEvents({
36131         /**
36132          * @event click
36133          * Fires when this item is clicked
36134          * @param {Roo.menu.BaseItem} this
36135          * @param {Roo.EventObject} e
36136          */
36137         click: true,
36138         /**
36139          * @event activate
36140          * Fires when this item is activated
36141          * @param {Roo.menu.BaseItem} this
36142          */
36143         activate : true,
36144         /**
36145          * @event deactivate
36146          * Fires when this item is deactivated
36147          * @param {Roo.menu.BaseItem} this
36148          */
36149         deactivate : true
36150     });
36151
36152     if(this.handler){
36153         this.on("click", this.handler, this.scope, true);
36154     }
36155 };
36156
36157 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36158     /**
36159      * @cfg {Function} handler
36160      * A function that will handle the click event of this menu item (defaults to undefined)
36161      */
36162     /**
36163      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36164      */
36165     canActivate : false,
36166     
36167      /**
36168      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36169      */
36170     hidden: false,
36171     
36172     /**
36173      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36174      */
36175     activeClass : "x-menu-item-active",
36176     /**
36177      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36178      */
36179     hideOnClick : true,
36180     /**
36181      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36182      */
36183     hideDelay : 100,
36184
36185     // private
36186     ctype: "Roo.menu.BaseItem",
36187
36188     // private
36189     actionMode : "container",
36190
36191     // private
36192     render : function(container, parentMenu){
36193         this.parentMenu = parentMenu;
36194         Roo.menu.BaseItem.superclass.render.call(this, container);
36195         this.container.menuItemId = this.id;
36196     },
36197
36198     // private
36199     onRender : function(container, position){
36200         this.el = Roo.get(this.el);
36201         container.dom.appendChild(this.el.dom);
36202     },
36203
36204     // private
36205     onClick : function(e){
36206         if(!this.disabled && this.fireEvent("click", this, e) !== false
36207                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36208             this.handleClick(e);
36209         }else{
36210             e.stopEvent();
36211         }
36212     },
36213
36214     // private
36215     activate : function(){
36216         if(this.disabled){
36217             return false;
36218         }
36219         var li = this.container;
36220         li.addClass(this.activeClass);
36221         this.region = li.getRegion().adjust(2, 2, -2, -2);
36222         this.fireEvent("activate", this);
36223         return true;
36224     },
36225
36226     // private
36227     deactivate : function(){
36228         this.container.removeClass(this.activeClass);
36229         this.fireEvent("deactivate", this);
36230     },
36231
36232     // private
36233     shouldDeactivate : function(e){
36234         return !this.region || !this.region.contains(e.getPoint());
36235     },
36236
36237     // private
36238     handleClick : function(e){
36239         if(this.hideOnClick){
36240             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36241         }
36242     },
36243
36244     // private
36245     expandMenu : function(autoActivate){
36246         // do nothing
36247     },
36248
36249     // private
36250     hideMenu : function(){
36251         // do nothing
36252     }
36253 });/*
36254  * Based on:
36255  * Ext JS Library 1.1.1
36256  * Copyright(c) 2006-2007, Ext JS, LLC.
36257  *
36258  * Originally Released Under LGPL - original licence link has changed is not relivant.
36259  *
36260  * Fork - LGPL
36261  * <script type="text/javascript">
36262  */
36263  
36264 /**
36265  * @class Roo.menu.Adapter
36266  * @extends Roo.menu.BaseItem
36267  * 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.
36268  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36269  * @constructor
36270  * Creates a new Adapter
36271  * @param {Object} config Configuration options
36272  */
36273 Roo.menu.Adapter = function(component, config){
36274     Roo.menu.Adapter.superclass.constructor.call(this, config);
36275     this.component = component;
36276 };
36277 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36278     // private
36279     canActivate : true,
36280
36281     // private
36282     onRender : function(container, position){
36283         this.component.render(container);
36284         this.el = this.component.getEl();
36285     },
36286
36287     // private
36288     activate : function(){
36289         if(this.disabled){
36290             return false;
36291         }
36292         this.component.focus();
36293         this.fireEvent("activate", this);
36294         return true;
36295     },
36296
36297     // private
36298     deactivate : function(){
36299         this.fireEvent("deactivate", this);
36300     },
36301
36302     // private
36303     disable : function(){
36304         this.component.disable();
36305         Roo.menu.Adapter.superclass.disable.call(this);
36306     },
36307
36308     // private
36309     enable : function(){
36310         this.component.enable();
36311         Roo.menu.Adapter.superclass.enable.call(this);
36312     }
36313 });/*
36314  * Based on:
36315  * Ext JS Library 1.1.1
36316  * Copyright(c) 2006-2007, Ext JS, LLC.
36317  *
36318  * Originally Released Under LGPL - original licence link has changed is not relivant.
36319  *
36320  * Fork - LGPL
36321  * <script type="text/javascript">
36322  */
36323
36324 /**
36325  * @class Roo.menu.TextItem
36326  * @extends Roo.menu.BaseItem
36327  * Adds a static text string to a menu, usually used as either a heading or group separator.
36328  * Note: old style constructor with text is still supported.
36329  * 
36330  * @constructor
36331  * Creates a new TextItem
36332  * @param {Object} cfg Configuration
36333  */
36334 Roo.menu.TextItem = function(cfg){
36335     if (typeof(cfg) == 'string') {
36336         this.text = cfg;
36337     } else {
36338         Roo.apply(this,cfg);
36339     }
36340     
36341     Roo.menu.TextItem.superclass.constructor.call(this);
36342 };
36343
36344 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36345     /**
36346      * @cfg {Boolean} text Text to show on item.
36347      */
36348     text : '',
36349     
36350     /**
36351      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36352      */
36353     hideOnClick : false,
36354     /**
36355      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36356      */
36357     itemCls : "x-menu-text",
36358
36359     // private
36360     onRender : function(){
36361         var s = document.createElement("span");
36362         s.className = this.itemCls;
36363         s.innerHTML = this.text;
36364         this.el = s;
36365         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36366     }
36367 });/*
36368  * Based on:
36369  * Ext JS Library 1.1.1
36370  * Copyright(c) 2006-2007, Ext JS, LLC.
36371  *
36372  * Originally Released Under LGPL - original licence link has changed is not relivant.
36373  *
36374  * Fork - LGPL
36375  * <script type="text/javascript">
36376  */
36377
36378 /**
36379  * @class Roo.menu.Separator
36380  * @extends Roo.menu.BaseItem
36381  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36382  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36383  * @constructor
36384  * @param {Object} config Configuration options
36385  */
36386 Roo.menu.Separator = function(config){
36387     Roo.menu.Separator.superclass.constructor.call(this, config);
36388 };
36389
36390 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36391     /**
36392      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36393      */
36394     itemCls : "x-menu-sep",
36395     /**
36396      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36397      */
36398     hideOnClick : false,
36399
36400     // private
36401     onRender : function(li){
36402         var s = document.createElement("span");
36403         s.className = this.itemCls;
36404         s.innerHTML = "&#160;";
36405         this.el = s;
36406         li.addClass("x-menu-sep-li");
36407         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36408     }
36409 });/*
36410  * Based on:
36411  * Ext JS Library 1.1.1
36412  * Copyright(c) 2006-2007, Ext JS, LLC.
36413  *
36414  * Originally Released Under LGPL - original licence link has changed is not relivant.
36415  *
36416  * Fork - LGPL
36417  * <script type="text/javascript">
36418  */
36419 /**
36420  * @class Roo.menu.Item
36421  * @extends Roo.menu.BaseItem
36422  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36423  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36424  * activation and click handling.
36425  * @constructor
36426  * Creates a new Item
36427  * @param {Object} config Configuration options
36428  */
36429 Roo.menu.Item = function(config){
36430     Roo.menu.Item.superclass.constructor.call(this, config);
36431     if(this.menu){
36432         this.menu = Roo.menu.MenuMgr.get(this.menu);
36433     }
36434 };
36435 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36436     
36437     /**
36438      * @cfg {String} text
36439      * The text to show on the menu item.
36440      */
36441     text: '',
36442      /**
36443      * @cfg {String} HTML to render in menu
36444      * The text to show on the menu item (HTML version).
36445      */
36446     html: '',
36447     /**
36448      * @cfg {String} icon
36449      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36450      */
36451     icon: undefined,
36452     /**
36453      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36454      */
36455     itemCls : "x-menu-item",
36456     /**
36457      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36458      */
36459     canActivate : true,
36460     /**
36461      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36462      */
36463     showDelay: 200,
36464     // doc'd in BaseItem
36465     hideDelay: 200,
36466
36467     // private
36468     ctype: "Roo.menu.Item",
36469     
36470     // private
36471     onRender : function(container, position){
36472         var el = document.createElement("a");
36473         el.hideFocus = true;
36474         el.unselectable = "on";
36475         el.href = this.href || "#";
36476         if(this.hrefTarget){
36477             el.target = this.hrefTarget;
36478         }
36479         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36480         
36481         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36482         
36483         el.innerHTML = String.format(
36484                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36485                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36486         this.el = el;
36487         Roo.menu.Item.superclass.onRender.call(this, container, position);
36488     },
36489
36490     /**
36491      * Sets the text to display in this menu item
36492      * @param {String} text The text to display
36493      * @param {Boolean} isHTML true to indicate text is pure html.
36494      */
36495     setText : function(text, isHTML){
36496         if (isHTML) {
36497             this.html = text;
36498         } else {
36499             this.text = text;
36500             this.html = '';
36501         }
36502         if(this.rendered){
36503             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36504      
36505             this.el.update(String.format(
36506                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36507                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36508             this.parentMenu.autoWidth();
36509         }
36510     },
36511
36512     // private
36513     handleClick : function(e){
36514         if(!this.href){ // if no link defined, stop the event automatically
36515             e.stopEvent();
36516         }
36517         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36518     },
36519
36520     // private
36521     activate : function(autoExpand){
36522         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36523             this.focus();
36524             if(autoExpand){
36525                 this.expandMenu();
36526             }
36527         }
36528         return true;
36529     },
36530
36531     // private
36532     shouldDeactivate : function(e){
36533         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36534             if(this.menu && this.menu.isVisible()){
36535                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36536             }
36537             return true;
36538         }
36539         return false;
36540     },
36541
36542     // private
36543     deactivate : function(){
36544         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36545         this.hideMenu();
36546     },
36547
36548     // private
36549     expandMenu : function(autoActivate){
36550         if(!this.disabled && this.menu){
36551             clearTimeout(this.hideTimer);
36552             delete this.hideTimer;
36553             if(!this.menu.isVisible() && !this.showTimer){
36554                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36555             }else if (this.menu.isVisible() && autoActivate){
36556                 this.menu.tryActivate(0, 1);
36557             }
36558         }
36559     },
36560
36561     // private
36562     deferExpand : function(autoActivate){
36563         delete this.showTimer;
36564         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36565         if(autoActivate){
36566             this.menu.tryActivate(0, 1);
36567         }
36568     },
36569
36570     // private
36571     hideMenu : function(){
36572         clearTimeout(this.showTimer);
36573         delete this.showTimer;
36574         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36575             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36576         }
36577     },
36578
36579     // private
36580     deferHide : function(){
36581         delete this.hideTimer;
36582         this.menu.hide();
36583     }
36584 });/*
36585  * Based on:
36586  * Ext JS Library 1.1.1
36587  * Copyright(c) 2006-2007, Ext JS, LLC.
36588  *
36589  * Originally Released Under LGPL - original licence link has changed is not relivant.
36590  *
36591  * Fork - LGPL
36592  * <script type="text/javascript">
36593  */
36594  
36595 /**
36596  * @class Roo.menu.CheckItem
36597  * @extends Roo.menu.Item
36598  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36599  * @constructor
36600  * Creates a new CheckItem
36601  * @param {Object} config Configuration options
36602  */
36603 Roo.menu.CheckItem = function(config){
36604     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36605     this.addEvents({
36606         /**
36607          * @event beforecheckchange
36608          * Fires before the checked value is set, providing an opportunity to cancel if needed
36609          * @param {Roo.menu.CheckItem} this
36610          * @param {Boolean} checked The new checked value that will be set
36611          */
36612         "beforecheckchange" : true,
36613         /**
36614          * @event checkchange
36615          * Fires after the checked value has been set
36616          * @param {Roo.menu.CheckItem} this
36617          * @param {Boolean} checked The checked value that was set
36618          */
36619         "checkchange" : true
36620     });
36621     if(this.checkHandler){
36622         this.on('checkchange', this.checkHandler, this.scope);
36623     }
36624 };
36625 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36626     /**
36627      * @cfg {String} group
36628      * All check items with the same group name will automatically be grouped into a single-select
36629      * radio button group (defaults to '')
36630      */
36631     /**
36632      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36633      */
36634     itemCls : "x-menu-item x-menu-check-item",
36635     /**
36636      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36637      */
36638     groupClass : "x-menu-group-item",
36639
36640     /**
36641      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36642      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36643      * initialized with checked = true will be rendered as checked.
36644      */
36645     checked: false,
36646
36647     // private
36648     ctype: "Roo.menu.CheckItem",
36649
36650     // private
36651     onRender : function(c){
36652         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36653         if(this.group){
36654             this.el.addClass(this.groupClass);
36655         }
36656         Roo.menu.MenuMgr.registerCheckable(this);
36657         if(this.checked){
36658             this.checked = false;
36659             this.setChecked(true, true);
36660         }
36661     },
36662
36663     // private
36664     destroy : function(){
36665         if(this.rendered){
36666             Roo.menu.MenuMgr.unregisterCheckable(this);
36667         }
36668         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36669     },
36670
36671     /**
36672      * Set the checked state of this item
36673      * @param {Boolean} checked The new checked value
36674      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36675      */
36676     setChecked : function(state, suppressEvent){
36677         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36678             if(this.container){
36679                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36680             }
36681             this.checked = state;
36682             if(suppressEvent !== true){
36683                 this.fireEvent("checkchange", this, state);
36684             }
36685         }
36686     },
36687
36688     // private
36689     handleClick : function(e){
36690        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36691            this.setChecked(!this.checked);
36692        }
36693        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36694     }
36695 });/*
36696  * Based on:
36697  * Ext JS Library 1.1.1
36698  * Copyright(c) 2006-2007, Ext JS, LLC.
36699  *
36700  * Originally Released Under LGPL - original licence link has changed is not relivant.
36701  *
36702  * Fork - LGPL
36703  * <script type="text/javascript">
36704  */
36705  
36706 /**
36707  * @class Roo.menu.DateItem
36708  * @extends Roo.menu.Adapter
36709  * A menu item that wraps the {@link Roo.DatPicker} component.
36710  * @constructor
36711  * Creates a new DateItem
36712  * @param {Object} config Configuration options
36713  */
36714 Roo.menu.DateItem = function(config){
36715     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36716     /** The Roo.DatePicker object @type Roo.DatePicker */
36717     this.picker = this.component;
36718     this.addEvents({select: true});
36719     
36720     this.picker.on("render", function(picker){
36721         picker.getEl().swallowEvent("click");
36722         picker.container.addClass("x-menu-date-item");
36723     });
36724
36725     this.picker.on("select", this.onSelect, this);
36726 };
36727
36728 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36729     // private
36730     onSelect : function(picker, date){
36731         this.fireEvent("select", this, date, picker);
36732         Roo.menu.DateItem.superclass.handleClick.call(this);
36733     }
36734 });/*
36735  * Based on:
36736  * Ext JS Library 1.1.1
36737  * Copyright(c) 2006-2007, Ext JS, LLC.
36738  *
36739  * Originally Released Under LGPL - original licence link has changed is not relivant.
36740  *
36741  * Fork - LGPL
36742  * <script type="text/javascript">
36743  */
36744  
36745 /**
36746  * @class Roo.menu.ColorItem
36747  * @extends Roo.menu.Adapter
36748  * A menu item that wraps the {@link Roo.ColorPalette} component.
36749  * @constructor
36750  * Creates a new ColorItem
36751  * @param {Object} config Configuration options
36752  */
36753 Roo.menu.ColorItem = function(config){
36754     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36755     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36756     this.palette = this.component;
36757     this.relayEvents(this.palette, ["select"]);
36758     if(this.selectHandler){
36759         this.on('select', this.selectHandler, this.scope);
36760     }
36761 };
36762 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36763  * Based on:
36764  * Ext JS Library 1.1.1
36765  * Copyright(c) 2006-2007, Ext JS, LLC.
36766  *
36767  * Originally Released Under LGPL - original licence link has changed is not relivant.
36768  *
36769  * Fork - LGPL
36770  * <script type="text/javascript">
36771  */
36772  
36773
36774 /**
36775  * @class Roo.menu.DateMenu
36776  * @extends Roo.menu.Menu
36777  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36778  * @constructor
36779  * Creates a new DateMenu
36780  * @param {Object} config Configuration options
36781  */
36782 Roo.menu.DateMenu = function(config){
36783     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36784     this.plain = true;
36785     var di = new Roo.menu.DateItem(config);
36786     this.add(di);
36787     /**
36788      * The {@link Roo.DatePicker} instance for this DateMenu
36789      * @type DatePicker
36790      */
36791     this.picker = di.picker;
36792     /**
36793      * @event select
36794      * @param {DatePicker} picker
36795      * @param {Date} date
36796      */
36797     this.relayEvents(di, ["select"]);
36798     this.on('beforeshow', function(){
36799         if(this.picker){
36800             this.picker.hideMonthPicker(false);
36801         }
36802     }, this);
36803 };
36804 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36805     cls:'x-date-menu'
36806 });/*
36807  * Based on:
36808  * Ext JS Library 1.1.1
36809  * Copyright(c) 2006-2007, Ext JS, LLC.
36810  *
36811  * Originally Released Under LGPL - original licence link has changed is not relivant.
36812  *
36813  * Fork - LGPL
36814  * <script type="text/javascript">
36815  */
36816  
36817
36818 /**
36819  * @class Roo.menu.ColorMenu
36820  * @extends Roo.menu.Menu
36821  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36822  * @constructor
36823  * Creates a new ColorMenu
36824  * @param {Object} config Configuration options
36825  */
36826 Roo.menu.ColorMenu = function(config){
36827     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36828     this.plain = true;
36829     var ci = new Roo.menu.ColorItem(config);
36830     this.add(ci);
36831     /**
36832      * The {@link Roo.ColorPalette} instance for this ColorMenu
36833      * @type ColorPalette
36834      */
36835     this.palette = ci.palette;
36836     /**
36837      * @event select
36838      * @param {ColorPalette} palette
36839      * @param {String} color
36840      */
36841     this.relayEvents(ci, ["select"]);
36842 };
36843 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36844  * Based on:
36845  * Ext JS Library 1.1.1
36846  * Copyright(c) 2006-2007, Ext JS, LLC.
36847  *
36848  * Originally Released Under LGPL - original licence link has changed is not relivant.
36849  *
36850  * Fork - LGPL
36851  * <script type="text/javascript">
36852  */
36853  
36854 /**
36855  * @class Roo.form.Field
36856  * @extends Roo.BoxComponent
36857  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36858  * @constructor
36859  * Creates a new Field
36860  * @param {Object} config Configuration options
36861  */
36862 Roo.form.Field = function(config){
36863     Roo.form.Field.superclass.constructor.call(this, config);
36864 };
36865
36866 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36867     /**
36868      * @cfg {String} fieldLabel Label to use when rendering a form.
36869      */
36870        /**
36871      * @cfg {String} qtip Mouse over tip
36872      */
36873      
36874     /**
36875      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36876      */
36877     invalidClass : "x-form-invalid",
36878     /**
36879      * @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")
36880      */
36881     invalidText : "The value in this field is invalid",
36882     /**
36883      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36884      */
36885     focusClass : "x-form-focus",
36886     /**
36887      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36888       automatic validation (defaults to "keyup").
36889      */
36890     validationEvent : "keyup",
36891     /**
36892      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36893      */
36894     validateOnBlur : true,
36895     /**
36896      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36897      */
36898     validationDelay : 250,
36899     /**
36900      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36901      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36902      */
36903     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36904     /**
36905      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36906      */
36907     fieldClass : "x-form-field",
36908     /**
36909      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36910      *<pre>
36911 Value         Description
36912 -----------   ----------------------------------------------------------------------
36913 qtip          Display a quick tip when the user hovers over the field
36914 title         Display a default browser title attribute popup
36915 under         Add a block div beneath the field containing the error text
36916 side          Add an error icon to the right of the field with a popup on hover
36917 [element id]  Add the error text directly to the innerHTML of the specified element
36918 </pre>
36919      */
36920     msgTarget : 'qtip',
36921     /**
36922      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36923      */
36924     msgFx : 'normal',
36925
36926     /**
36927      * @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.
36928      */
36929     readOnly : false,
36930
36931     /**
36932      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36933      */
36934     disabled : false,
36935
36936     /**
36937      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36938      */
36939     inputType : undefined,
36940     
36941     /**
36942      * @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).
36943          */
36944         tabIndex : undefined,
36945         
36946     // private
36947     isFormField : true,
36948
36949     // private
36950     hasFocus : false,
36951     /**
36952      * @property {Roo.Element} fieldEl
36953      * Element Containing the rendered Field (with label etc.)
36954      */
36955     /**
36956      * @cfg {Mixed} value A value to initialize this field with.
36957      */
36958     value : undefined,
36959
36960     /**
36961      * @cfg {String} name The field's HTML name attribute.
36962      */
36963     /**
36964      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36965      */
36966
36967         // private ??
36968         initComponent : function(){
36969         Roo.form.Field.superclass.initComponent.call(this);
36970         this.addEvents({
36971             /**
36972              * @event focus
36973              * Fires when this field receives input focus.
36974              * @param {Roo.form.Field} this
36975              */
36976             focus : true,
36977             /**
36978              * @event blur
36979              * Fires when this field loses input focus.
36980              * @param {Roo.form.Field} this
36981              */
36982             blur : true,
36983             /**
36984              * @event specialkey
36985              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36986              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36987              * @param {Roo.form.Field} this
36988              * @param {Roo.EventObject} e The event object
36989              */
36990             specialkey : true,
36991             /**
36992              * @event change
36993              * Fires just before the field blurs if the field value has changed.
36994              * @param {Roo.form.Field} this
36995              * @param {Mixed} newValue The new value
36996              * @param {Mixed} oldValue The original value
36997              */
36998             change : true,
36999             /**
37000              * @event invalid
37001              * Fires after the field has been marked as invalid.
37002              * @param {Roo.form.Field} this
37003              * @param {String} msg The validation message
37004              */
37005             invalid : true,
37006             /**
37007              * @event valid
37008              * Fires after the field has been validated with no errors.
37009              * @param {Roo.form.Field} this
37010              */
37011             valid : true,
37012              /**
37013              * @event keyup
37014              * Fires after the key up
37015              * @param {Roo.form.Field} this
37016              * @param {Roo.EventObject}  e The event Object
37017              */
37018             keyup : true
37019         });
37020     },
37021
37022     /**
37023      * Returns the name attribute of the field if available
37024      * @return {String} name The field name
37025      */
37026     getName: function(){
37027          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37028     },
37029
37030     // private
37031     onRender : function(ct, position){
37032         Roo.form.Field.superclass.onRender.call(this, ct, position);
37033         if(!this.el){
37034             var cfg = this.getAutoCreate();
37035             if(!cfg.name){
37036                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37037             }
37038             if (!cfg.name.length) {
37039                 delete cfg.name;
37040             }
37041             if(this.inputType){
37042                 cfg.type = this.inputType;
37043             }
37044             this.el = ct.createChild(cfg, position);
37045         }
37046         var type = this.el.dom.type;
37047         if(type){
37048             if(type == 'password'){
37049                 type = 'text';
37050             }
37051             this.el.addClass('x-form-'+type);
37052         }
37053         if(this.readOnly){
37054             this.el.dom.readOnly = true;
37055         }
37056         if(this.tabIndex !== undefined){
37057             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37058         }
37059
37060         this.el.addClass([this.fieldClass, this.cls]);
37061         this.initValue();
37062     },
37063
37064     /**
37065      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37066      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37067      * @return {Roo.form.Field} this
37068      */
37069     applyTo : function(target){
37070         this.allowDomMove = false;
37071         this.el = Roo.get(target);
37072         this.render(this.el.dom.parentNode);
37073         return this;
37074     },
37075
37076     // private
37077     initValue : function(){
37078         if(this.value !== undefined){
37079             this.setValue(this.value);
37080         }else if(this.el.dom.value.length > 0){
37081             this.setValue(this.el.dom.value);
37082         }
37083     },
37084
37085     /**
37086      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37087      */
37088     isDirty : function() {
37089         if(this.disabled) {
37090             return false;
37091         }
37092         return String(this.getValue()) !== String(this.originalValue);
37093     },
37094
37095     // private
37096     afterRender : function(){
37097         Roo.form.Field.superclass.afterRender.call(this);
37098         this.initEvents();
37099     },
37100
37101     // private
37102     fireKey : function(e){
37103         //Roo.log('field ' + e.getKey());
37104         if(e.isNavKeyPress()){
37105             this.fireEvent("specialkey", this, e);
37106         }
37107     },
37108
37109     /**
37110      * Resets the current field value to the originally loaded value and clears any validation messages
37111      */
37112     reset : function(){
37113         this.setValue(this.resetValue);
37114         this.clearInvalid();
37115     },
37116
37117     // private
37118     initEvents : function(){
37119         // safari killled keypress - so keydown is now used..
37120         this.el.on("keydown" , this.fireKey,  this);
37121         this.el.on("focus", this.onFocus,  this);
37122         this.el.on("blur", this.onBlur,  this);
37123         this.el.relayEvent('keyup', this);
37124
37125         // reference to original value for reset
37126         this.originalValue = this.getValue();
37127         this.resetValue =  this.getValue();
37128     },
37129
37130     // private
37131     onFocus : function(){
37132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37133             this.el.addClass(this.focusClass);
37134         }
37135         if(!this.hasFocus){
37136             this.hasFocus = true;
37137             this.startValue = this.getValue();
37138             this.fireEvent("focus", this);
37139         }
37140     },
37141
37142     beforeBlur : Roo.emptyFn,
37143
37144     // private
37145     onBlur : function(){
37146         this.beforeBlur();
37147         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37148             this.el.removeClass(this.focusClass);
37149         }
37150         this.hasFocus = false;
37151         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37152             this.validate();
37153         }
37154         var v = this.getValue();
37155         if(String(v) !== String(this.startValue)){
37156             this.fireEvent('change', this, v, this.startValue);
37157         }
37158         this.fireEvent("blur", this);
37159     },
37160
37161     /**
37162      * Returns whether or not the field value is currently valid
37163      * @param {Boolean} preventMark True to disable marking the field invalid
37164      * @return {Boolean} True if the value is valid, else false
37165      */
37166     isValid : function(preventMark){
37167         if(this.disabled){
37168             return true;
37169         }
37170         var restore = this.preventMark;
37171         this.preventMark = preventMark === true;
37172         var v = this.validateValue(this.processValue(this.getRawValue()));
37173         this.preventMark = restore;
37174         return v;
37175     },
37176
37177     /**
37178      * Validates the field value
37179      * @return {Boolean} True if the value is valid, else false
37180      */
37181     validate : function(){
37182         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37183             this.clearInvalid();
37184             return true;
37185         }
37186         return false;
37187     },
37188
37189     processValue : function(value){
37190         return value;
37191     },
37192
37193     // private
37194     // Subclasses should provide the validation implementation by overriding this
37195     validateValue : function(value){
37196         return true;
37197     },
37198
37199     /**
37200      * Mark this field as invalid
37201      * @param {String} msg The validation message
37202      */
37203     markInvalid : function(msg){
37204         if(!this.rendered || this.preventMark){ // not rendered
37205             return;
37206         }
37207         
37208         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37209         
37210         obj.el.addClass(this.invalidClass);
37211         msg = msg || this.invalidText;
37212         switch(this.msgTarget){
37213             case 'qtip':
37214                 obj.el.dom.qtip = msg;
37215                 obj.el.dom.qclass = 'x-form-invalid-tip';
37216                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37217                     Roo.QuickTips.enable();
37218                 }
37219                 break;
37220             case 'title':
37221                 this.el.dom.title = msg;
37222                 break;
37223             case 'under':
37224                 if(!this.errorEl){
37225                     var elp = this.el.findParent('.x-form-element', 5, true);
37226                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37227                     this.errorEl.setWidth(elp.getWidth(true)-20);
37228                 }
37229                 this.errorEl.update(msg);
37230                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37231                 break;
37232             case 'side':
37233                 if(!this.errorIcon){
37234                     var elp = this.el.findParent('.x-form-element', 5, true);
37235                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37236                 }
37237                 this.alignErrorIcon();
37238                 this.errorIcon.dom.qtip = msg;
37239                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37240                 this.errorIcon.show();
37241                 this.on('resize', this.alignErrorIcon, this);
37242                 break;
37243             default:
37244                 var t = Roo.getDom(this.msgTarget);
37245                 t.innerHTML = msg;
37246                 t.style.display = this.msgDisplay;
37247                 break;
37248         }
37249         this.fireEvent('invalid', this, msg);
37250     },
37251
37252     // private
37253     alignErrorIcon : function(){
37254         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37255     },
37256
37257     /**
37258      * Clear any invalid styles/messages for this field
37259      */
37260     clearInvalid : function(){
37261         if(!this.rendered || this.preventMark){ // not rendered
37262             return;
37263         }
37264         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37265         
37266         obj.el.removeClass(this.invalidClass);
37267         switch(this.msgTarget){
37268             case 'qtip':
37269                 obj.el.dom.qtip = '';
37270                 break;
37271             case 'title':
37272                 this.el.dom.title = '';
37273                 break;
37274             case 'under':
37275                 if(this.errorEl){
37276                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37277                 }
37278                 break;
37279             case 'side':
37280                 if(this.errorIcon){
37281                     this.errorIcon.dom.qtip = '';
37282                     this.errorIcon.hide();
37283                     this.un('resize', this.alignErrorIcon, this);
37284                 }
37285                 break;
37286             default:
37287                 var t = Roo.getDom(this.msgTarget);
37288                 t.innerHTML = '';
37289                 t.style.display = 'none';
37290                 break;
37291         }
37292         this.fireEvent('valid', this);
37293     },
37294
37295     /**
37296      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37297      * @return {Mixed} value The field value
37298      */
37299     getRawValue : function(){
37300         var v = this.el.getValue();
37301         
37302         return v;
37303     },
37304
37305     /**
37306      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37307      * @return {Mixed} value The field value
37308      */
37309     getValue : function(){
37310         var v = this.el.getValue();
37311          
37312         return v;
37313     },
37314
37315     /**
37316      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37317      * @param {Mixed} value The value to set
37318      */
37319     setRawValue : function(v){
37320         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37321     },
37322
37323     /**
37324      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37325      * @param {Mixed} value The value to set
37326      */
37327     setValue : function(v){
37328         this.value = v;
37329         if(this.rendered){
37330             this.el.dom.value = (v === null || v === undefined ? '' : v);
37331              this.validate();
37332         }
37333     },
37334
37335     adjustSize : function(w, h){
37336         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37337         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37338         return s;
37339     },
37340
37341     adjustWidth : function(tag, w){
37342         tag = tag.toLowerCase();
37343         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37344             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37345                 if(tag == 'input'){
37346                     return w + 2;
37347                 }
37348                 if(tag == 'textarea'){
37349                     return w-2;
37350                 }
37351             }else if(Roo.isOpera){
37352                 if(tag == 'input'){
37353                     return w + 2;
37354                 }
37355                 if(tag == 'textarea'){
37356                     return w-2;
37357                 }
37358             }
37359         }
37360         return w;
37361     }
37362 });
37363
37364
37365 // anything other than normal should be considered experimental
37366 Roo.form.Field.msgFx = {
37367     normal : {
37368         show: function(msgEl, f){
37369             msgEl.setDisplayed('block');
37370         },
37371
37372         hide : function(msgEl, f){
37373             msgEl.setDisplayed(false).update('');
37374         }
37375     },
37376
37377     slide : {
37378         show: function(msgEl, f){
37379             msgEl.slideIn('t', {stopFx:true});
37380         },
37381
37382         hide : function(msgEl, f){
37383             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37384         }
37385     },
37386
37387     slideRight : {
37388         show: function(msgEl, f){
37389             msgEl.fixDisplay();
37390             msgEl.alignTo(f.el, 'tl-tr');
37391             msgEl.slideIn('l', {stopFx:true});
37392         },
37393
37394         hide : function(msgEl, f){
37395             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37396         }
37397     }
37398 };/*
37399  * Based on:
37400  * Ext JS Library 1.1.1
37401  * Copyright(c) 2006-2007, Ext JS, LLC.
37402  *
37403  * Originally Released Under LGPL - original licence link has changed is not relivant.
37404  *
37405  * Fork - LGPL
37406  * <script type="text/javascript">
37407  */
37408  
37409
37410 /**
37411  * @class Roo.form.TextField
37412  * @extends Roo.form.Field
37413  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37414  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37415  * @constructor
37416  * Creates a new TextField
37417  * @param {Object} config Configuration options
37418  */
37419 Roo.form.TextField = function(config){
37420     Roo.form.TextField.superclass.constructor.call(this, config);
37421     this.addEvents({
37422         /**
37423          * @event autosize
37424          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37425          * according to the default logic, but this event provides a hook for the developer to apply additional
37426          * logic at runtime to resize the field if needed.
37427              * @param {Roo.form.Field} this This text field
37428              * @param {Number} width The new field width
37429              */
37430         autosize : true
37431     });
37432 };
37433
37434 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37435     /**
37436      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37437      */
37438     grow : false,
37439     /**
37440      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37441      */
37442     growMin : 30,
37443     /**
37444      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37445      */
37446     growMax : 800,
37447     /**
37448      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37449      */
37450     vtype : null,
37451     /**
37452      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37453      */
37454     maskRe : null,
37455     /**
37456      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37457      */
37458     disableKeyFilter : false,
37459     /**
37460      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37461      */
37462     allowBlank : true,
37463     /**
37464      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37465      */
37466     minLength : 0,
37467     /**
37468      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37469      */
37470     maxLength : Number.MAX_VALUE,
37471     /**
37472      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37473      */
37474     minLengthText : "The minimum length for this field is {0}",
37475     /**
37476      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37477      */
37478     maxLengthText : "The maximum length for this field is {0}",
37479     /**
37480      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37481      */
37482     selectOnFocus : false,
37483     /**
37484      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37485      */
37486     blankText : "This field is required",
37487     /**
37488      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37489      * If available, this function will be called only after the basic validators all return true, and will be passed the
37490      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37491      */
37492     validator : null,
37493     /**
37494      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37495      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37496      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37497      */
37498     regex : null,
37499     /**
37500      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37501      */
37502     regexText : "",
37503     /**
37504      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37505      */
37506     emptyText : null,
37507    
37508
37509     // private
37510     initEvents : function()
37511     {
37512         if (this.emptyText) {
37513             this.el.attr('placeholder', this.emptyText);
37514         }
37515         
37516         Roo.form.TextField.superclass.initEvents.call(this);
37517         if(this.validationEvent == 'keyup'){
37518             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37519             this.el.on('keyup', this.filterValidation, this);
37520         }
37521         else if(this.validationEvent !== false){
37522             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37523         }
37524         
37525         if(this.selectOnFocus){
37526             this.on("focus", this.preFocus, this);
37527             
37528         }
37529         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37530             this.el.on("keypress", this.filterKeys, this);
37531         }
37532         if(this.grow){
37533             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37534             this.el.on("click", this.autoSize,  this);
37535         }
37536         if(this.el.is('input[type=password]') && Roo.isSafari){
37537             this.el.on('keydown', this.SafariOnKeyDown, this);
37538         }
37539     },
37540
37541     processValue : function(value){
37542         if(this.stripCharsRe){
37543             var newValue = value.replace(this.stripCharsRe, '');
37544             if(newValue !== value){
37545                 this.setRawValue(newValue);
37546                 return newValue;
37547             }
37548         }
37549         return value;
37550     },
37551
37552     filterValidation : function(e){
37553         if(!e.isNavKeyPress()){
37554             this.validationTask.delay(this.validationDelay);
37555         }
37556     },
37557
37558     // private
37559     onKeyUp : function(e){
37560         if(!e.isNavKeyPress()){
37561             this.autoSize();
37562         }
37563     },
37564
37565     /**
37566      * Resets the current field value to the originally-loaded value and clears any validation messages.
37567      *  
37568      */
37569     reset : function(){
37570         Roo.form.TextField.superclass.reset.call(this);
37571        
37572     },
37573
37574     
37575     // private
37576     preFocus : function(){
37577         
37578         if(this.selectOnFocus){
37579             this.el.dom.select();
37580         }
37581     },
37582
37583     
37584     // private
37585     filterKeys : function(e){
37586         var k = e.getKey();
37587         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37588             return;
37589         }
37590         var c = e.getCharCode(), cc = String.fromCharCode(c);
37591         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37592             return;
37593         }
37594         if(!this.maskRe.test(cc)){
37595             e.stopEvent();
37596         }
37597     },
37598
37599     setValue : function(v){
37600         
37601         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37602         
37603         this.autoSize();
37604     },
37605
37606     /**
37607      * Validates a value according to the field's validation rules and marks the field as invalid
37608      * if the validation fails
37609      * @param {Mixed} value The value to validate
37610      * @return {Boolean} True if the value is valid, else false
37611      */
37612     validateValue : function(value){
37613         if(value.length < 1)  { // if it's blank
37614              if(this.allowBlank){
37615                 this.clearInvalid();
37616                 return true;
37617              }else{
37618                 this.markInvalid(this.blankText);
37619                 return false;
37620              }
37621         }
37622         if(value.length < this.minLength){
37623             this.markInvalid(String.format(this.minLengthText, this.minLength));
37624             return false;
37625         }
37626         if(value.length > this.maxLength){
37627             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37628             return false;
37629         }
37630         if(this.vtype){
37631             var vt = Roo.form.VTypes;
37632             if(!vt[this.vtype](value, this)){
37633                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37634                 return false;
37635             }
37636         }
37637         if(typeof this.validator == "function"){
37638             var msg = this.validator(value);
37639             if(msg !== true){
37640                 this.markInvalid(msg);
37641                 return false;
37642             }
37643         }
37644         if(this.regex && !this.regex.test(value)){
37645             this.markInvalid(this.regexText);
37646             return false;
37647         }
37648         return true;
37649     },
37650
37651     /**
37652      * Selects text in this field
37653      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37654      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37655      */
37656     selectText : function(start, end){
37657         var v = this.getRawValue();
37658         if(v.length > 0){
37659             start = start === undefined ? 0 : start;
37660             end = end === undefined ? v.length : end;
37661             var d = this.el.dom;
37662             if(d.setSelectionRange){
37663                 d.setSelectionRange(start, end);
37664             }else if(d.createTextRange){
37665                 var range = d.createTextRange();
37666                 range.moveStart("character", start);
37667                 range.moveEnd("character", v.length-end);
37668                 range.select();
37669             }
37670         }
37671     },
37672
37673     /**
37674      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37675      * This only takes effect if grow = true, and fires the autosize event.
37676      */
37677     autoSize : function(){
37678         if(!this.grow || !this.rendered){
37679             return;
37680         }
37681         if(!this.metrics){
37682             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37683         }
37684         var el = this.el;
37685         var v = el.dom.value;
37686         var d = document.createElement('div');
37687         d.appendChild(document.createTextNode(v));
37688         v = d.innerHTML;
37689         d = null;
37690         v += "&#160;";
37691         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37692         this.el.setWidth(w);
37693         this.fireEvent("autosize", this, w);
37694     },
37695     
37696     // private
37697     SafariOnKeyDown : function(event)
37698     {
37699         // this is a workaround for a password hang bug on chrome/ webkit.
37700         
37701         var isSelectAll = false;
37702         
37703         if(this.el.dom.selectionEnd > 0){
37704             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37705         }
37706         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37707             event.preventDefault();
37708             this.setValue('');
37709             return;
37710         }
37711         
37712         if(isSelectAll){ // backspace and delete key
37713             
37714             event.preventDefault();
37715             // this is very hacky as keydown always get's upper case.
37716             //
37717             var cc = String.fromCharCode(event.getCharCode());
37718             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37719             
37720         }
37721         
37722         
37723     }
37724 });/*
37725  * Based on:
37726  * Ext JS Library 1.1.1
37727  * Copyright(c) 2006-2007, Ext JS, LLC.
37728  *
37729  * Originally Released Under LGPL - original licence link has changed is not relivant.
37730  *
37731  * Fork - LGPL
37732  * <script type="text/javascript">
37733  */
37734  
37735 /**
37736  * @class Roo.form.Hidden
37737  * @extends Roo.form.TextField
37738  * Simple Hidden element used on forms 
37739  * 
37740  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37741  * 
37742  * @constructor
37743  * Creates a new Hidden form element.
37744  * @param {Object} config Configuration options
37745  */
37746
37747
37748
37749 // easy hidden field...
37750 Roo.form.Hidden = function(config){
37751     Roo.form.Hidden.superclass.constructor.call(this, config);
37752 };
37753   
37754 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37755     fieldLabel:      '',
37756     inputType:      'hidden',
37757     width:          50,
37758     allowBlank:     true,
37759     labelSeparator: '',
37760     hidden:         true,
37761     itemCls :       'x-form-item-display-none'
37762
37763
37764 });
37765
37766
37767 /*
37768  * Based on:
37769  * Ext JS Library 1.1.1
37770  * Copyright(c) 2006-2007, Ext JS, LLC.
37771  *
37772  * Originally Released Under LGPL - original licence link has changed is not relivant.
37773  *
37774  * Fork - LGPL
37775  * <script type="text/javascript">
37776  */
37777  
37778 /**
37779  * @class Roo.form.TriggerField
37780  * @extends Roo.form.TextField
37781  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37782  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37783  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37784  * for which you can provide a custom implementation.  For example:
37785  * <pre><code>
37786 var trigger = new Roo.form.TriggerField();
37787 trigger.onTriggerClick = myTriggerFn;
37788 trigger.applyTo('my-field');
37789 </code></pre>
37790  *
37791  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37792  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37793  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37794  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37795  * @constructor
37796  * Create a new TriggerField.
37797  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37798  * to the base TextField)
37799  */
37800 Roo.form.TriggerField = function(config){
37801     this.mimicing = false;
37802     Roo.form.TriggerField.superclass.constructor.call(this, config);
37803 };
37804
37805 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37806     /**
37807      * @cfg {String} triggerClass A CSS class to apply to the trigger
37808      */
37809     /**
37810      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37811      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37812      */
37813     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37814     /**
37815      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37816      */
37817     hideTrigger:false,
37818
37819     /** @cfg {Boolean} grow @hide */
37820     /** @cfg {Number} growMin @hide */
37821     /** @cfg {Number} growMax @hide */
37822
37823     /**
37824      * @hide 
37825      * @method
37826      */
37827     autoSize: Roo.emptyFn,
37828     // private
37829     monitorTab : true,
37830     // private
37831     deferHeight : true,
37832
37833     
37834     actionMode : 'wrap',
37835     // private
37836     onResize : function(w, h){
37837         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37838         if(typeof w == 'number'){
37839             var x = w - this.trigger.getWidth();
37840             this.el.setWidth(this.adjustWidth('input', x));
37841             this.trigger.setStyle('left', x+'px');
37842         }
37843     },
37844
37845     // private
37846     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37847
37848     // private
37849     getResizeEl : function(){
37850         return this.wrap;
37851     },
37852
37853     // private
37854     getPositionEl : function(){
37855         return this.wrap;
37856     },
37857
37858     // private
37859     alignErrorIcon : function(){
37860         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37861     },
37862
37863     // private
37864     onRender : function(ct, position){
37865         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37866         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37867         this.trigger = this.wrap.createChild(this.triggerConfig ||
37868                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37869         if(this.hideTrigger){
37870             this.trigger.setDisplayed(false);
37871         }
37872         this.initTrigger();
37873         if(!this.width){
37874             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37875         }
37876     },
37877
37878     // private
37879     initTrigger : function(){
37880         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37881         this.trigger.addClassOnOver('x-form-trigger-over');
37882         this.trigger.addClassOnClick('x-form-trigger-click');
37883     },
37884
37885     // private
37886     onDestroy : function(){
37887         if(this.trigger){
37888             this.trigger.removeAllListeners();
37889             this.trigger.remove();
37890         }
37891         if(this.wrap){
37892             this.wrap.remove();
37893         }
37894         Roo.form.TriggerField.superclass.onDestroy.call(this);
37895     },
37896
37897     // private
37898     onFocus : function(){
37899         Roo.form.TriggerField.superclass.onFocus.call(this);
37900         if(!this.mimicing){
37901             this.wrap.addClass('x-trigger-wrap-focus');
37902             this.mimicing = true;
37903             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37904             if(this.monitorTab){
37905                 this.el.on("keydown", this.checkTab, this);
37906             }
37907         }
37908     },
37909
37910     // private
37911     checkTab : function(e){
37912         if(e.getKey() == e.TAB){
37913             this.triggerBlur();
37914         }
37915     },
37916
37917     // private
37918     onBlur : function(){
37919         // do nothing
37920     },
37921
37922     // private
37923     mimicBlur : function(e, t){
37924         if(!this.wrap.contains(t) && this.validateBlur()){
37925             this.triggerBlur();
37926         }
37927     },
37928
37929     // private
37930     triggerBlur : function(){
37931         this.mimicing = false;
37932         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37933         if(this.monitorTab){
37934             this.el.un("keydown", this.checkTab, this);
37935         }
37936         this.wrap.removeClass('x-trigger-wrap-focus');
37937         Roo.form.TriggerField.superclass.onBlur.call(this);
37938     },
37939
37940     // private
37941     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37942     validateBlur : function(e, t){
37943         return true;
37944     },
37945
37946     // private
37947     onDisable : function(){
37948         Roo.form.TriggerField.superclass.onDisable.call(this);
37949         if(this.wrap){
37950             this.wrap.addClass('x-item-disabled');
37951         }
37952     },
37953
37954     // private
37955     onEnable : function(){
37956         Roo.form.TriggerField.superclass.onEnable.call(this);
37957         if(this.wrap){
37958             this.wrap.removeClass('x-item-disabled');
37959         }
37960     },
37961
37962     // private
37963     onShow : function(){
37964         var ae = this.getActionEl();
37965         
37966         if(ae){
37967             ae.dom.style.display = '';
37968             ae.dom.style.visibility = 'visible';
37969         }
37970     },
37971
37972     // private
37973     
37974     onHide : function(){
37975         var ae = this.getActionEl();
37976         ae.dom.style.display = 'none';
37977     },
37978
37979     /**
37980      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37981      * by an implementing function.
37982      * @method
37983      * @param {EventObject} e
37984      */
37985     onTriggerClick : Roo.emptyFn
37986 });
37987
37988 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37989 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37990 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37991 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37992     initComponent : function(){
37993         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37994
37995         this.triggerConfig = {
37996             tag:'span', cls:'x-form-twin-triggers', cn:[
37997             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37998             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37999         ]};
38000     },
38001
38002     getTrigger : function(index){
38003         return this.triggers[index];
38004     },
38005
38006     initTrigger : function(){
38007         var ts = this.trigger.select('.x-form-trigger', true);
38008         this.wrap.setStyle('overflow', 'hidden');
38009         var triggerField = this;
38010         ts.each(function(t, all, index){
38011             t.hide = function(){
38012                 var w = triggerField.wrap.getWidth();
38013                 this.dom.style.display = 'none';
38014                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38015             };
38016             t.show = function(){
38017                 var w = triggerField.wrap.getWidth();
38018                 this.dom.style.display = '';
38019                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38020             };
38021             var triggerIndex = 'Trigger'+(index+1);
38022
38023             if(this['hide'+triggerIndex]){
38024                 t.dom.style.display = 'none';
38025             }
38026             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38027             t.addClassOnOver('x-form-trigger-over');
38028             t.addClassOnClick('x-form-trigger-click');
38029         }, this);
38030         this.triggers = ts.elements;
38031     },
38032
38033     onTrigger1Click : Roo.emptyFn,
38034     onTrigger2Click : Roo.emptyFn
38035 });/*
38036  * Based on:
38037  * Ext JS Library 1.1.1
38038  * Copyright(c) 2006-2007, Ext JS, LLC.
38039  *
38040  * Originally Released Under LGPL - original licence link has changed is not relivant.
38041  *
38042  * Fork - LGPL
38043  * <script type="text/javascript">
38044  */
38045  
38046 /**
38047  * @class Roo.form.TextArea
38048  * @extends Roo.form.TextField
38049  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38050  * support for auto-sizing.
38051  * @constructor
38052  * Creates a new TextArea
38053  * @param {Object} config Configuration options
38054  */
38055 Roo.form.TextArea = function(config){
38056     Roo.form.TextArea.superclass.constructor.call(this, config);
38057     // these are provided exchanges for backwards compat
38058     // minHeight/maxHeight were replaced by growMin/growMax to be
38059     // compatible with TextField growing config values
38060     if(this.minHeight !== undefined){
38061         this.growMin = this.minHeight;
38062     }
38063     if(this.maxHeight !== undefined){
38064         this.growMax = this.maxHeight;
38065     }
38066 };
38067
38068 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38069     /**
38070      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38071      */
38072     growMin : 60,
38073     /**
38074      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38075      */
38076     growMax: 1000,
38077     /**
38078      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38079      * in the field (equivalent to setting overflow: hidden, defaults to false)
38080      */
38081     preventScrollbars: false,
38082     /**
38083      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38084      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38085      */
38086
38087     // private
38088     onRender : function(ct, position){
38089         if(!this.el){
38090             this.defaultAutoCreate = {
38091                 tag: "textarea",
38092                 style:"width:300px;height:60px;",
38093                 autocomplete: "off"
38094             };
38095         }
38096         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38097         if(this.grow){
38098             this.textSizeEl = Roo.DomHelper.append(document.body, {
38099                 tag: "pre", cls: "x-form-grow-sizer"
38100             });
38101             if(this.preventScrollbars){
38102                 this.el.setStyle("overflow", "hidden");
38103             }
38104             this.el.setHeight(this.growMin);
38105         }
38106     },
38107
38108     onDestroy : function(){
38109         if(this.textSizeEl){
38110             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38111         }
38112         Roo.form.TextArea.superclass.onDestroy.call(this);
38113     },
38114
38115     // private
38116     onKeyUp : function(e){
38117         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38118             this.autoSize();
38119         }
38120     },
38121
38122     /**
38123      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38124      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38125      */
38126     autoSize : function(){
38127         if(!this.grow || !this.textSizeEl){
38128             return;
38129         }
38130         var el = this.el;
38131         var v = el.dom.value;
38132         var ts = this.textSizeEl;
38133
38134         ts.innerHTML = '';
38135         ts.appendChild(document.createTextNode(v));
38136         v = ts.innerHTML;
38137
38138         Roo.fly(ts).setWidth(this.el.getWidth());
38139         if(v.length < 1){
38140             v = "&#160;&#160;";
38141         }else{
38142             if(Roo.isIE){
38143                 v = v.replace(/\n/g, '<p>&#160;</p>');
38144             }
38145             v += "&#160;\n&#160;";
38146         }
38147         ts.innerHTML = v;
38148         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38149         if(h != this.lastHeight){
38150             this.lastHeight = h;
38151             this.el.setHeight(h);
38152             this.fireEvent("autosize", this, h);
38153         }
38154     }
38155 });/*
38156  * Based on:
38157  * Ext JS Library 1.1.1
38158  * Copyright(c) 2006-2007, Ext JS, LLC.
38159  *
38160  * Originally Released Under LGPL - original licence link has changed is not relivant.
38161  *
38162  * Fork - LGPL
38163  * <script type="text/javascript">
38164  */
38165  
38166
38167 /**
38168  * @class Roo.form.NumberField
38169  * @extends Roo.form.TextField
38170  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38171  * @constructor
38172  * Creates a new NumberField
38173  * @param {Object} config Configuration options
38174  */
38175 Roo.form.NumberField = function(config){
38176     Roo.form.NumberField.superclass.constructor.call(this, config);
38177 };
38178
38179 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38180     /**
38181      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38182      */
38183     fieldClass: "x-form-field x-form-num-field",
38184     /**
38185      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38186      */
38187     allowDecimals : true,
38188     /**
38189      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38190      */
38191     decimalSeparator : ".",
38192     /**
38193      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38194      */
38195     decimalPrecision : 2,
38196     /**
38197      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38198      */
38199     allowNegative : true,
38200     /**
38201      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38202      */
38203     minValue : Number.NEGATIVE_INFINITY,
38204     /**
38205      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38206      */
38207     maxValue : Number.MAX_VALUE,
38208     /**
38209      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38210      */
38211     minText : "The minimum value for this field is {0}",
38212     /**
38213      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38214      */
38215     maxText : "The maximum value for this field is {0}",
38216     /**
38217      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38218      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38219      */
38220     nanText : "{0} is not a valid number",
38221
38222     // private
38223     initEvents : function(){
38224         Roo.form.NumberField.superclass.initEvents.call(this);
38225         var allowed = "0123456789";
38226         if(this.allowDecimals){
38227             allowed += this.decimalSeparator;
38228         }
38229         if(this.allowNegative){
38230             allowed += "-";
38231         }
38232         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38233         var keyPress = function(e){
38234             var k = e.getKey();
38235             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38236                 return;
38237             }
38238             var c = e.getCharCode();
38239             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38240                 e.stopEvent();
38241             }
38242         };
38243         this.el.on("keypress", keyPress, this);
38244     },
38245
38246     // private
38247     validateValue : function(value){
38248         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38249             return false;
38250         }
38251         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38252              return true;
38253         }
38254         var num = this.parseValue(value);
38255         if(isNaN(num)){
38256             this.markInvalid(String.format(this.nanText, value));
38257             return false;
38258         }
38259         if(num < this.minValue){
38260             this.markInvalid(String.format(this.minText, this.minValue));
38261             return false;
38262         }
38263         if(num > this.maxValue){
38264             this.markInvalid(String.format(this.maxText, this.maxValue));
38265             return false;
38266         }
38267         return true;
38268     },
38269
38270     getValue : function(){
38271         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38272     },
38273
38274     // private
38275     parseValue : function(value){
38276         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38277         return isNaN(value) ? '' : value;
38278     },
38279
38280     // private
38281     fixPrecision : function(value){
38282         var nan = isNaN(value);
38283         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38284             return nan ? '' : value;
38285         }
38286         return parseFloat(value).toFixed(this.decimalPrecision);
38287     },
38288
38289     setValue : function(v){
38290         v = this.fixPrecision(v);
38291         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38292     },
38293
38294     // private
38295     decimalPrecisionFcn : function(v){
38296         return Math.floor(v);
38297     },
38298
38299     beforeBlur : function(){
38300         var v = this.parseValue(this.getRawValue());
38301         if(v){
38302             this.setValue(v);
38303         }
38304     }
38305 });/*
38306  * Based on:
38307  * Ext JS Library 1.1.1
38308  * Copyright(c) 2006-2007, Ext JS, LLC.
38309  *
38310  * Originally Released Under LGPL - original licence link has changed is not relivant.
38311  *
38312  * Fork - LGPL
38313  * <script type="text/javascript">
38314  */
38315  
38316 /**
38317  * @class Roo.form.DateField
38318  * @extends Roo.form.TriggerField
38319  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38320 * @constructor
38321 * Create a new DateField
38322 * @param {Object} config
38323  */
38324 Roo.form.DateField = function(config){
38325     Roo.form.DateField.superclass.constructor.call(this, config);
38326     
38327       this.addEvents({
38328          
38329         /**
38330          * @event select
38331          * Fires when a date is selected
38332              * @param {Roo.form.DateField} combo This combo box
38333              * @param {Date} date The date selected
38334              */
38335         'select' : true
38336          
38337     });
38338     
38339     
38340     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38341     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38342     this.ddMatch = null;
38343     if(this.disabledDates){
38344         var dd = this.disabledDates;
38345         var re = "(?:";
38346         for(var i = 0; i < dd.length; i++){
38347             re += dd[i];
38348             if(i != dd.length-1) re += "|";
38349         }
38350         this.ddMatch = new RegExp(re + ")");
38351     }
38352 };
38353
38354 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38355     /**
38356      * @cfg {String} format
38357      * The default date format string which can be overriden for localization support.  The format must be
38358      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38359      */
38360     format : "m/d/y",
38361     /**
38362      * @cfg {String} altFormats
38363      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38364      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38365      */
38366     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38367     /**
38368      * @cfg {Array} disabledDays
38369      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38370      */
38371     disabledDays : null,
38372     /**
38373      * @cfg {String} disabledDaysText
38374      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38375      */
38376     disabledDaysText : "Disabled",
38377     /**
38378      * @cfg {Array} disabledDates
38379      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38380      * expression so they are very powerful. Some examples:
38381      * <ul>
38382      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38383      * <li>["03/08", "09/16"] would disable those days for every year</li>
38384      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38385      * <li>["03/../2006"] would disable every day in March 2006</li>
38386      * <li>["^03"] would disable every day in every March</li>
38387      * </ul>
38388      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38389      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38390      */
38391     disabledDates : null,
38392     /**
38393      * @cfg {String} disabledDatesText
38394      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38395      */
38396     disabledDatesText : "Disabled",
38397     /**
38398      * @cfg {Date/String} minValue
38399      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38400      * valid format (defaults to null).
38401      */
38402     minValue : null,
38403     /**
38404      * @cfg {Date/String} maxValue
38405      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38406      * valid format (defaults to null).
38407      */
38408     maxValue : null,
38409     /**
38410      * @cfg {String} minText
38411      * The error text to display when the date in the cell is before minValue (defaults to
38412      * 'The date in this field must be after {minValue}').
38413      */
38414     minText : "The date in this field must be equal to or after {0}",
38415     /**
38416      * @cfg {String} maxText
38417      * The error text to display when the date in the cell is after maxValue (defaults to
38418      * 'The date in this field must be before {maxValue}').
38419      */
38420     maxText : "The date in this field must be equal to or before {0}",
38421     /**
38422      * @cfg {String} invalidText
38423      * The error text to display when the date in the field is invalid (defaults to
38424      * '{value} is not a valid date - it must be in the format {format}').
38425      */
38426     invalidText : "{0} is not a valid date - it must be in the format {1}",
38427     /**
38428      * @cfg {String} triggerClass
38429      * An additional CSS class used to style the trigger button.  The trigger will always get the
38430      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38431      * which displays a calendar icon).
38432      */
38433     triggerClass : 'x-form-date-trigger',
38434     
38435
38436     /**
38437      * @cfg {Boolean} useIso
38438      * if enabled, then the date field will use a hidden field to store the 
38439      * real value as iso formated date. default (false)
38440      */ 
38441     useIso : false,
38442     /**
38443      * @cfg {String/Object} autoCreate
38444      * A DomHelper element spec, or true for a default element spec (defaults to
38445      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38446      */ 
38447     // private
38448     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38449     
38450     // private
38451     hiddenField: false,
38452     
38453     onRender : function(ct, position)
38454     {
38455         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38456         if (this.useIso) {
38457             //this.el.dom.removeAttribute('name'); 
38458             Roo.log("Changing name?");
38459             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38460             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38461                     'before', true);
38462             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38463             // prevent input submission
38464             this.hiddenName = this.name;
38465         }
38466             
38467             
38468     },
38469     
38470     // private
38471     validateValue : function(value)
38472     {
38473         value = this.formatDate(value);
38474         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38475             Roo.log('super failed');
38476             return false;
38477         }
38478         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38479              return true;
38480         }
38481         var svalue = value;
38482         value = this.parseDate(value);
38483         if(!value){
38484             Roo.log('parse date failed' + svalue);
38485             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38486             return false;
38487         }
38488         var time = value.getTime();
38489         if(this.minValue && time < this.minValue.getTime()){
38490             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38491             return false;
38492         }
38493         if(this.maxValue && time > this.maxValue.getTime()){
38494             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38495             return false;
38496         }
38497         if(this.disabledDays){
38498             var day = value.getDay();
38499             for(var i = 0; i < this.disabledDays.length; i++) {
38500                 if(day === this.disabledDays[i]){
38501                     this.markInvalid(this.disabledDaysText);
38502                     return false;
38503                 }
38504             }
38505         }
38506         var fvalue = this.formatDate(value);
38507         if(this.ddMatch && this.ddMatch.test(fvalue)){
38508             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38509             return false;
38510         }
38511         return true;
38512     },
38513
38514     // private
38515     // Provides logic to override the default TriggerField.validateBlur which just returns true
38516     validateBlur : function(){
38517         return !this.menu || !this.menu.isVisible();
38518     },
38519     
38520     getName: function()
38521     {
38522         // returns hidden if it's set..
38523         if (!this.rendered) {return ''};
38524         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38525         
38526     },
38527
38528     /**
38529      * Returns the current date value of the date field.
38530      * @return {Date} The date value
38531      */
38532     getValue : function(){
38533         
38534         return  this.hiddenField ?
38535                 this.hiddenField.value :
38536                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38537     },
38538
38539     /**
38540      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38541      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38542      * (the default format used is "m/d/y").
38543      * <br />Usage:
38544      * <pre><code>
38545 //All of these calls set the same date value (May 4, 2006)
38546
38547 //Pass a date object:
38548 var dt = new Date('5/4/06');
38549 dateField.setValue(dt);
38550
38551 //Pass a date string (default format):
38552 dateField.setValue('5/4/06');
38553
38554 //Pass a date string (custom format):
38555 dateField.format = 'Y-m-d';
38556 dateField.setValue('2006-5-4');
38557 </code></pre>
38558      * @param {String/Date} date The date or valid date string
38559      */
38560     setValue : function(date){
38561         if (this.hiddenField) {
38562             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38563         }
38564         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38565         // make sure the value field is always stored as a date..
38566         this.value = this.parseDate(date);
38567         
38568         
38569     },
38570
38571     // private
38572     parseDate : function(value){
38573         if(!value || value instanceof Date){
38574             return value;
38575         }
38576         var v = Date.parseDate(value, this.format);
38577          if (!v && this.useIso) {
38578             v = Date.parseDate(value, 'Y-m-d');
38579         }
38580         if(!v && this.altFormats){
38581             if(!this.altFormatsArray){
38582                 this.altFormatsArray = this.altFormats.split("|");
38583             }
38584             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38585                 v = Date.parseDate(value, this.altFormatsArray[i]);
38586             }
38587         }
38588         return v;
38589     },
38590
38591     // private
38592     formatDate : function(date, fmt){
38593         return (!date || !(date instanceof Date)) ?
38594                date : date.dateFormat(fmt || this.format);
38595     },
38596
38597     // private
38598     menuListeners : {
38599         select: function(m, d){
38600             
38601             this.setValue(d);
38602             this.fireEvent('select', this, d);
38603         },
38604         show : function(){ // retain focus styling
38605             this.onFocus();
38606         },
38607         hide : function(){
38608             this.focus.defer(10, this);
38609             var ml = this.menuListeners;
38610             this.menu.un("select", ml.select,  this);
38611             this.menu.un("show", ml.show,  this);
38612             this.menu.un("hide", ml.hide,  this);
38613         }
38614     },
38615
38616     // private
38617     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38618     onTriggerClick : function(){
38619         if(this.disabled){
38620             return;
38621         }
38622         if(this.menu == null){
38623             this.menu = new Roo.menu.DateMenu();
38624         }
38625         Roo.apply(this.menu.picker,  {
38626             showClear: this.allowBlank,
38627             minDate : this.minValue,
38628             maxDate : this.maxValue,
38629             disabledDatesRE : this.ddMatch,
38630             disabledDatesText : this.disabledDatesText,
38631             disabledDays : this.disabledDays,
38632             disabledDaysText : this.disabledDaysText,
38633             format : this.useIso ? 'Y-m-d' : this.format,
38634             minText : String.format(this.minText, this.formatDate(this.minValue)),
38635             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38636         });
38637         this.menu.on(Roo.apply({}, this.menuListeners, {
38638             scope:this
38639         }));
38640         this.menu.picker.setValue(this.getValue() || new Date());
38641         this.menu.show(this.el, "tl-bl?");
38642     },
38643
38644     beforeBlur : function(){
38645         var v = this.parseDate(this.getRawValue());
38646         if(v){
38647             this.setValue(v);
38648         }
38649     },
38650
38651     /*@
38652      * overide
38653      * 
38654      */
38655     isDirty : function() {
38656         if(this.disabled) {
38657             return false;
38658         }
38659         
38660         if(typeof(this.startValue) === 'undefined'){
38661             return false;
38662         }
38663         
38664         return String(this.getValue()) !== String(this.startValue);
38665         
38666     }
38667 });/*
38668  * Based on:
38669  * Ext JS Library 1.1.1
38670  * Copyright(c) 2006-2007, Ext JS, LLC.
38671  *
38672  * Originally Released Under LGPL - original licence link has changed is not relivant.
38673  *
38674  * Fork - LGPL
38675  * <script type="text/javascript">
38676  */
38677  
38678 /**
38679  * @class Roo.form.MonthField
38680  * @extends Roo.form.TriggerField
38681  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38682 * @constructor
38683 * Create a new MonthField
38684 * @param {Object} config
38685  */
38686 Roo.form.MonthField = function(config){
38687     
38688     Roo.form.MonthField.superclass.constructor.call(this, config);
38689     
38690       this.addEvents({
38691          
38692         /**
38693          * @event select
38694          * Fires when a date is selected
38695              * @param {Roo.form.MonthFieeld} combo This combo box
38696              * @param {Date} date The date selected
38697              */
38698         'select' : true
38699          
38700     });
38701     
38702     
38703     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38704     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38705     this.ddMatch = null;
38706     if(this.disabledDates){
38707         var dd = this.disabledDates;
38708         var re = "(?:";
38709         for(var i = 0; i < dd.length; i++){
38710             re += dd[i];
38711             if(i != dd.length-1) re += "|";
38712         }
38713         this.ddMatch = new RegExp(re + ")");
38714     }
38715 };
38716
38717 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38718     /**
38719      * @cfg {String} format
38720      * The default date format string which can be overriden for localization support.  The format must be
38721      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38722      */
38723     format : "M Y",
38724     /**
38725      * @cfg {String} altFormats
38726      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38727      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38728      */
38729     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38730     /**
38731      * @cfg {Array} disabledDays
38732      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38733      */
38734     disabledDays : [0,1,2,3,4,5,6],
38735     /**
38736      * @cfg {String} disabledDaysText
38737      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38738      */
38739     disabledDaysText : "Disabled",
38740     /**
38741      * @cfg {Array} disabledDates
38742      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38743      * expression so they are very powerful. Some examples:
38744      * <ul>
38745      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38746      * <li>["03/08", "09/16"] would disable those days for every year</li>
38747      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38748      * <li>["03/../2006"] would disable every day in March 2006</li>
38749      * <li>["^03"] would disable every day in every March</li>
38750      * </ul>
38751      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38752      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38753      */
38754     disabledDates : null,
38755     /**
38756      * @cfg {String} disabledDatesText
38757      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38758      */
38759     disabledDatesText : "Disabled",
38760     /**
38761      * @cfg {Date/String} minValue
38762      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38763      * valid format (defaults to null).
38764      */
38765     minValue : null,
38766     /**
38767      * @cfg {Date/String} maxValue
38768      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38769      * valid format (defaults to null).
38770      */
38771     maxValue : null,
38772     /**
38773      * @cfg {String} minText
38774      * The error text to display when the date in the cell is before minValue (defaults to
38775      * 'The date in this field must be after {minValue}').
38776      */
38777     minText : "The date in this field must be equal to or after {0}",
38778     /**
38779      * @cfg {String} maxTextf
38780      * The error text to display when the date in the cell is after maxValue (defaults to
38781      * 'The date in this field must be before {maxValue}').
38782      */
38783     maxText : "The date in this field must be equal to or before {0}",
38784     /**
38785      * @cfg {String} invalidText
38786      * The error text to display when the date in the field is invalid (defaults to
38787      * '{value} is not a valid date - it must be in the format {format}').
38788      */
38789     invalidText : "{0} is not a valid date - it must be in the format {1}",
38790     /**
38791      * @cfg {String} triggerClass
38792      * An additional CSS class used to style the trigger button.  The trigger will always get the
38793      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38794      * which displays a calendar icon).
38795      */
38796     triggerClass : 'x-form-date-trigger',
38797     
38798
38799     /**
38800      * @cfg {Boolean} useIso
38801      * if enabled, then the date field will use a hidden field to store the 
38802      * real value as iso formated date. default (true)
38803      */ 
38804     useIso : true,
38805     /**
38806      * @cfg {String/Object} autoCreate
38807      * A DomHelper element spec, or true for a default element spec (defaults to
38808      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38809      */ 
38810     // private
38811     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38812     
38813     // private
38814     hiddenField: false,
38815     
38816     hideMonthPicker : false,
38817     
38818     onRender : function(ct, position)
38819     {
38820         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38821         if (this.useIso) {
38822             this.el.dom.removeAttribute('name'); 
38823             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38824                     'before', true);
38825             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38826             // prevent input submission
38827             this.hiddenName = this.name;
38828         }
38829             
38830             
38831     },
38832     
38833     // private
38834     validateValue : function(value)
38835     {
38836         value = this.formatDate(value);
38837         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38838             return false;
38839         }
38840         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38841              return true;
38842         }
38843         var svalue = value;
38844         value = this.parseDate(value);
38845         if(!value){
38846             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38847             return false;
38848         }
38849         var time = value.getTime();
38850         if(this.minValue && time < this.minValue.getTime()){
38851             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38852             return false;
38853         }
38854         if(this.maxValue && time > this.maxValue.getTime()){
38855             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38856             return false;
38857         }
38858         /*if(this.disabledDays){
38859             var day = value.getDay();
38860             for(var i = 0; i < this.disabledDays.length; i++) {
38861                 if(day === this.disabledDays[i]){
38862                     this.markInvalid(this.disabledDaysText);
38863                     return false;
38864                 }
38865             }
38866         }
38867         */
38868         var fvalue = this.formatDate(value);
38869         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38870             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38871             return false;
38872         }
38873         */
38874         return true;
38875     },
38876
38877     // private
38878     // Provides logic to override the default TriggerField.validateBlur which just returns true
38879     validateBlur : function(){
38880         return !this.menu || !this.menu.isVisible();
38881     },
38882
38883     /**
38884      * Returns the current date value of the date field.
38885      * @return {Date} The date value
38886      */
38887     getValue : function(){
38888         
38889         
38890         
38891         return  this.hiddenField ?
38892                 this.hiddenField.value :
38893                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38894     },
38895
38896     /**
38897      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38898      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38899      * (the default format used is "m/d/y").
38900      * <br />Usage:
38901      * <pre><code>
38902 //All of these calls set the same date value (May 4, 2006)
38903
38904 //Pass a date object:
38905 var dt = new Date('5/4/06');
38906 monthField.setValue(dt);
38907
38908 //Pass a date string (default format):
38909 monthField.setValue('5/4/06');
38910
38911 //Pass a date string (custom format):
38912 monthField.format = 'Y-m-d';
38913 monthField.setValue('2006-5-4');
38914 </code></pre>
38915      * @param {String/Date} date The date or valid date string
38916      */
38917     setValue : function(date){
38918         Roo.log('month setValue' + date);
38919         // can only be first of month..
38920         
38921         var val = this.parseDate(date);
38922         
38923         if (this.hiddenField) {
38924             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38925         }
38926         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38927         this.value = this.parseDate(date);
38928     },
38929
38930     // private
38931     parseDate : function(value){
38932         if(!value || value instanceof Date){
38933             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38934             return value;
38935         }
38936         var v = Date.parseDate(value, this.format);
38937         if (!v && this.useIso) {
38938             v = Date.parseDate(value, 'Y-m-d');
38939         }
38940         if (v) {
38941             // 
38942             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38943         }
38944         
38945         
38946         if(!v && this.altFormats){
38947             if(!this.altFormatsArray){
38948                 this.altFormatsArray = this.altFormats.split("|");
38949             }
38950             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38951                 v = Date.parseDate(value, this.altFormatsArray[i]);
38952             }
38953         }
38954         return v;
38955     },
38956
38957     // private
38958     formatDate : function(date, fmt){
38959         return (!date || !(date instanceof Date)) ?
38960                date : date.dateFormat(fmt || this.format);
38961     },
38962
38963     // private
38964     menuListeners : {
38965         select: function(m, d){
38966             this.setValue(d);
38967             this.fireEvent('select', this, d);
38968         },
38969         show : function(){ // retain focus styling
38970             this.onFocus();
38971         },
38972         hide : function(){
38973             this.focus.defer(10, this);
38974             var ml = this.menuListeners;
38975             this.menu.un("select", ml.select,  this);
38976             this.menu.un("show", ml.show,  this);
38977             this.menu.un("hide", ml.hide,  this);
38978         }
38979     },
38980     // private
38981     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38982     onTriggerClick : function(){
38983         if(this.disabled){
38984             return;
38985         }
38986         if(this.menu == null){
38987             this.menu = new Roo.menu.DateMenu();
38988            
38989         }
38990         
38991         Roo.apply(this.menu.picker,  {
38992             
38993             showClear: this.allowBlank,
38994             minDate : this.minValue,
38995             maxDate : this.maxValue,
38996             disabledDatesRE : this.ddMatch,
38997             disabledDatesText : this.disabledDatesText,
38998             
38999             format : this.useIso ? 'Y-m-d' : this.format,
39000             minText : String.format(this.minText, this.formatDate(this.minValue)),
39001             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39002             
39003         });
39004          this.menu.on(Roo.apply({}, this.menuListeners, {
39005             scope:this
39006         }));
39007        
39008         
39009         var m = this.menu;
39010         var p = m.picker;
39011         
39012         // hide month picker get's called when we called by 'before hide';
39013         
39014         var ignorehide = true;
39015         p.hideMonthPicker  = function(disableAnim){
39016             if (ignorehide) {
39017                 return;
39018             }
39019              if(this.monthPicker){
39020                 Roo.log("hideMonthPicker called");
39021                 if(disableAnim === true){
39022                     this.monthPicker.hide();
39023                 }else{
39024                     this.monthPicker.slideOut('t', {duration:.2});
39025                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39026                     p.fireEvent("select", this, this.value);
39027                     m.hide();
39028                 }
39029             }
39030         }
39031         
39032         Roo.log('picker set value');
39033         Roo.log(this.getValue());
39034         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39035         m.show(this.el, 'tl-bl?');
39036         ignorehide  = false;
39037         // this will trigger hideMonthPicker..
39038         
39039         
39040         // hidden the day picker
39041         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39042         
39043         
39044         
39045       
39046         
39047         p.showMonthPicker.defer(100, p);
39048     
39049         
39050        
39051     },
39052
39053     beforeBlur : function(){
39054         var v = this.parseDate(this.getRawValue());
39055         if(v){
39056             this.setValue(v);
39057         }
39058     }
39059
39060     /** @cfg {Boolean} grow @hide */
39061     /** @cfg {Number} growMin @hide */
39062     /** @cfg {Number} growMax @hide */
39063     /**
39064      * @hide
39065      * @method autoSize
39066      */
39067 });/*
39068  * Based on:
39069  * Ext JS Library 1.1.1
39070  * Copyright(c) 2006-2007, Ext JS, LLC.
39071  *
39072  * Originally Released Under LGPL - original licence link has changed is not relivant.
39073  *
39074  * Fork - LGPL
39075  * <script type="text/javascript">
39076  */
39077  
39078
39079 /**
39080  * @class Roo.form.ComboBox
39081  * @extends Roo.form.TriggerField
39082  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39083  * @constructor
39084  * Create a new ComboBox.
39085  * @param {Object} config Configuration options
39086  */
39087 Roo.form.ComboBox = function(config){
39088     Roo.form.ComboBox.superclass.constructor.call(this, config);
39089     this.addEvents({
39090         /**
39091          * @event expand
39092          * Fires when the dropdown list is expanded
39093              * @param {Roo.form.ComboBox} combo This combo box
39094              */
39095         'expand' : true,
39096         /**
39097          * @event collapse
39098          * Fires when the dropdown list is collapsed
39099              * @param {Roo.form.ComboBox} combo This combo box
39100              */
39101         'collapse' : true,
39102         /**
39103          * @event beforeselect
39104          * Fires before a list item is selected. Return false to cancel the selection.
39105              * @param {Roo.form.ComboBox} combo This combo box
39106              * @param {Roo.data.Record} record The data record returned from the underlying store
39107              * @param {Number} index The index of the selected item in the dropdown list
39108              */
39109         'beforeselect' : true,
39110         /**
39111          * @event select
39112          * Fires when a list item is selected
39113              * @param {Roo.form.ComboBox} combo This combo box
39114              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39115              * @param {Number} index The index of the selected item in the dropdown list
39116              */
39117         'select' : true,
39118         /**
39119          * @event beforequery
39120          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39121          * The event object passed has these properties:
39122              * @param {Roo.form.ComboBox} combo This combo box
39123              * @param {String} query The query
39124              * @param {Boolean} forceAll true to force "all" query
39125              * @param {Boolean} cancel true to cancel the query
39126              * @param {Object} e The query event object
39127              */
39128         'beforequery': true,
39129          /**
39130          * @event add
39131          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39132              * @param {Roo.form.ComboBox} combo This combo box
39133              */
39134         'add' : true,
39135         /**
39136          * @event edit
39137          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39138              * @param {Roo.form.ComboBox} combo This combo box
39139              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39140              */
39141         'edit' : true
39142         
39143         
39144     });
39145     if(this.transform){
39146         this.allowDomMove = false;
39147         var s = Roo.getDom(this.transform);
39148         if(!this.hiddenName){
39149             this.hiddenName = s.name;
39150         }
39151         if(!this.store){
39152             this.mode = 'local';
39153             var d = [], opts = s.options;
39154             for(var i = 0, len = opts.length;i < len; i++){
39155                 var o = opts[i];
39156                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39157                 if(o.selected) {
39158                     this.value = value;
39159                 }
39160                 d.push([value, o.text]);
39161             }
39162             this.store = new Roo.data.SimpleStore({
39163                 'id': 0,
39164                 fields: ['value', 'text'],
39165                 data : d
39166             });
39167             this.valueField = 'value';
39168             this.displayField = 'text';
39169         }
39170         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39171         if(!this.lazyRender){
39172             this.target = true;
39173             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39174             s.parentNode.removeChild(s); // remove it
39175             this.render(this.el.parentNode);
39176         }else{
39177             s.parentNode.removeChild(s); // remove it
39178         }
39179
39180     }
39181     if (this.store) {
39182         this.store = Roo.factory(this.store, Roo.data);
39183     }
39184     
39185     this.selectedIndex = -1;
39186     if(this.mode == 'local'){
39187         if(config.queryDelay === undefined){
39188             this.queryDelay = 10;
39189         }
39190         if(config.minChars === undefined){
39191             this.minChars = 0;
39192         }
39193     }
39194 };
39195
39196 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39197     /**
39198      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39199      */
39200     /**
39201      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39202      * rendering into an Roo.Editor, defaults to false)
39203      */
39204     /**
39205      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39206      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39207      */
39208     /**
39209      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39210      */
39211     /**
39212      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39213      * the dropdown list (defaults to undefined, with no header element)
39214      */
39215
39216      /**
39217      * @cfg {String/Roo.Template} tpl The template to use to render the output
39218      */
39219      
39220     // private
39221     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39222     /**
39223      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39224      */
39225     listWidth: undefined,
39226     /**
39227      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39228      * mode = 'remote' or 'text' if mode = 'local')
39229      */
39230     displayField: undefined,
39231     /**
39232      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39233      * mode = 'remote' or 'value' if mode = 'local'). 
39234      * Note: use of a valueField requires the user make a selection
39235      * in order for a value to be mapped.
39236      */
39237     valueField: undefined,
39238     
39239     
39240     /**
39241      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39242      * field's data value (defaults to the underlying DOM element's name)
39243      */
39244     hiddenName: undefined,
39245     /**
39246      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39247      */
39248     listClass: '',
39249     /**
39250      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39251      */
39252     selectedClass: 'x-combo-selected',
39253     /**
39254      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39255      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39256      * which displays a downward arrow icon).
39257      */
39258     triggerClass : 'x-form-arrow-trigger',
39259     /**
39260      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39261      */
39262     shadow:'sides',
39263     /**
39264      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39265      * anchor positions (defaults to 'tl-bl')
39266      */
39267     listAlign: 'tl-bl?',
39268     /**
39269      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39270      */
39271     maxHeight: 300,
39272     /**
39273      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39274      * query specified by the allQuery config option (defaults to 'query')
39275      */
39276     triggerAction: 'query',
39277     /**
39278      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39279      * (defaults to 4, does not apply if editable = false)
39280      */
39281     minChars : 4,
39282     /**
39283      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39284      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39285      */
39286     typeAhead: false,
39287     /**
39288      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39289      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39290      */
39291     queryDelay: 500,
39292     /**
39293      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39294      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39295      */
39296     pageSize: 0,
39297     /**
39298      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39299      * when editable = true (defaults to false)
39300      */
39301     selectOnFocus:false,
39302     /**
39303      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39304      */
39305     queryParam: 'query',
39306     /**
39307      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39308      * when mode = 'remote' (defaults to 'Loading...')
39309      */
39310     loadingText: 'Loading...',
39311     /**
39312      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39313      */
39314     resizable: false,
39315     /**
39316      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39317      */
39318     handleHeight : 8,
39319     /**
39320      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39321      * traditional select (defaults to true)
39322      */
39323     editable: true,
39324     /**
39325      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39326      */
39327     allQuery: '',
39328     /**
39329      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39330      */
39331     mode: 'remote',
39332     /**
39333      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39334      * listWidth has a higher value)
39335      */
39336     minListWidth : 70,
39337     /**
39338      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39339      * allow the user to set arbitrary text into the field (defaults to false)
39340      */
39341     forceSelection:false,
39342     /**
39343      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39344      * if typeAhead = true (defaults to 250)
39345      */
39346     typeAheadDelay : 250,
39347     /**
39348      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39349      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39350      */
39351     valueNotFoundText : undefined,
39352     /**
39353      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39354      */
39355     blockFocus : false,
39356     
39357     /**
39358      * @cfg {Boolean} disableClear Disable showing of clear button.
39359      */
39360     disableClear : false,
39361     /**
39362      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39363      */
39364     alwaysQuery : false,
39365     
39366     //private
39367     addicon : false,
39368     editicon: false,
39369     
39370     // element that contains real text value.. (when hidden is used..)
39371      
39372     // private
39373     onRender : function(ct, position){
39374         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39375         if(this.hiddenName){
39376             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39377                     'before', true);
39378             this.hiddenField.value =
39379                 this.hiddenValue !== undefined ? this.hiddenValue :
39380                 this.value !== undefined ? this.value : '';
39381
39382             // prevent input submission
39383             this.el.dom.removeAttribute('name');
39384              
39385              
39386         }
39387         if(Roo.isGecko){
39388             this.el.dom.setAttribute('autocomplete', 'off');
39389         }
39390
39391         var cls = 'x-combo-list';
39392
39393         this.list = new Roo.Layer({
39394             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39395         });
39396
39397         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39398         this.list.setWidth(lw);
39399         this.list.swallowEvent('mousewheel');
39400         this.assetHeight = 0;
39401
39402         if(this.title){
39403             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39404             this.assetHeight += this.header.getHeight();
39405         }
39406
39407         this.innerList = this.list.createChild({cls:cls+'-inner'});
39408         this.innerList.on('mouseover', this.onViewOver, this);
39409         this.innerList.on('mousemove', this.onViewMove, this);
39410         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39411         
39412         if(this.allowBlank && !this.pageSize && !this.disableClear){
39413             this.footer = this.list.createChild({cls:cls+'-ft'});
39414             this.pageTb = new Roo.Toolbar(this.footer);
39415            
39416         }
39417         if(this.pageSize){
39418             this.footer = this.list.createChild({cls:cls+'-ft'});
39419             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39420                     {pageSize: this.pageSize});
39421             
39422         }
39423         
39424         if (this.pageTb && this.allowBlank && !this.disableClear) {
39425             var _this = this;
39426             this.pageTb.add(new Roo.Toolbar.Fill(), {
39427                 cls: 'x-btn-icon x-btn-clear',
39428                 text: '&#160;',
39429                 handler: function()
39430                 {
39431                     _this.collapse();
39432                     _this.clearValue();
39433                     _this.onSelect(false, -1);
39434                 }
39435             });
39436         }
39437         if (this.footer) {
39438             this.assetHeight += this.footer.getHeight();
39439         }
39440         
39441
39442         if(!this.tpl){
39443             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39444         }
39445
39446         this.view = new Roo.View(this.innerList, this.tpl, {
39447             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39448         });
39449
39450         this.view.on('click', this.onViewClick, this);
39451
39452         this.store.on('beforeload', this.onBeforeLoad, this);
39453         this.store.on('load', this.onLoad, this);
39454         this.store.on('loadexception', this.onLoadException, this);
39455
39456         if(this.resizable){
39457             this.resizer = new Roo.Resizable(this.list,  {
39458                pinned:true, handles:'se'
39459             });
39460             this.resizer.on('resize', function(r, w, h){
39461                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39462                 this.listWidth = w;
39463                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39464                 this.restrictHeight();
39465             }, this);
39466             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39467         }
39468         if(!this.editable){
39469             this.editable = true;
39470             this.setEditable(false);
39471         }  
39472         
39473         
39474         if (typeof(this.events.add.listeners) != 'undefined') {
39475             
39476             this.addicon = this.wrap.createChild(
39477                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39478        
39479             this.addicon.on('click', function(e) {
39480                 this.fireEvent('add', this);
39481             }, this);
39482         }
39483         if (typeof(this.events.edit.listeners) != 'undefined') {
39484             
39485             this.editicon = this.wrap.createChild(
39486                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39487             if (this.addicon) {
39488                 this.editicon.setStyle('margin-left', '40px');
39489             }
39490             this.editicon.on('click', function(e) {
39491                 
39492                 // we fire even  if inothing is selected..
39493                 this.fireEvent('edit', this, this.lastData );
39494                 
39495             }, this);
39496         }
39497         
39498         
39499         
39500     },
39501
39502     // private
39503     initEvents : function(){
39504         Roo.form.ComboBox.superclass.initEvents.call(this);
39505
39506         this.keyNav = new Roo.KeyNav(this.el, {
39507             "up" : function(e){
39508                 this.inKeyMode = true;
39509                 this.selectPrev();
39510             },
39511
39512             "down" : function(e){
39513                 if(!this.isExpanded()){
39514                     this.onTriggerClick();
39515                 }else{
39516                     this.inKeyMode = true;
39517                     this.selectNext();
39518                 }
39519             },
39520
39521             "enter" : function(e){
39522                 this.onViewClick();
39523                 //return true;
39524             },
39525
39526             "esc" : function(e){
39527                 this.collapse();
39528             },
39529
39530             "tab" : function(e){
39531                 this.onViewClick(false);
39532                 this.fireEvent("specialkey", this, e);
39533                 return true;
39534             },
39535
39536             scope : this,
39537
39538             doRelay : function(foo, bar, hname){
39539                 if(hname == 'down' || this.scope.isExpanded()){
39540                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39541                 }
39542                 return true;
39543             },
39544
39545             forceKeyDown: true
39546         });
39547         this.queryDelay = Math.max(this.queryDelay || 10,
39548                 this.mode == 'local' ? 10 : 250);
39549         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39550         if(this.typeAhead){
39551             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39552         }
39553         if(this.editable !== false){
39554             this.el.on("keyup", this.onKeyUp, this);
39555         }
39556         if(this.forceSelection){
39557             this.on('blur', this.doForce, this);
39558         }
39559     },
39560
39561     onDestroy : function(){
39562         if(this.view){
39563             this.view.setStore(null);
39564             this.view.el.removeAllListeners();
39565             this.view.el.remove();
39566             this.view.purgeListeners();
39567         }
39568         if(this.list){
39569             this.list.destroy();
39570         }
39571         if(this.store){
39572             this.store.un('beforeload', this.onBeforeLoad, this);
39573             this.store.un('load', this.onLoad, this);
39574             this.store.un('loadexception', this.onLoadException, this);
39575         }
39576         Roo.form.ComboBox.superclass.onDestroy.call(this);
39577     },
39578
39579     // private
39580     fireKey : function(e){
39581         if(e.isNavKeyPress() && !this.list.isVisible()){
39582             this.fireEvent("specialkey", this, e);
39583         }
39584     },
39585
39586     // private
39587     onResize: function(w, h){
39588         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39589         
39590         if(typeof w != 'number'){
39591             // we do not handle it!?!?
39592             return;
39593         }
39594         var tw = this.trigger.getWidth();
39595         tw += this.addicon ? this.addicon.getWidth() : 0;
39596         tw += this.editicon ? this.editicon.getWidth() : 0;
39597         var x = w - tw;
39598         this.el.setWidth( this.adjustWidth('input', x));
39599             
39600         this.trigger.setStyle('left', x+'px');
39601         
39602         if(this.list && this.listWidth === undefined){
39603             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39604             this.list.setWidth(lw);
39605             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39606         }
39607         
39608     
39609         
39610     },
39611
39612     /**
39613      * Allow or prevent the user from directly editing the field text.  If false is passed,
39614      * the user will only be able to select from the items defined in the dropdown list.  This method
39615      * is the runtime equivalent of setting the 'editable' config option at config time.
39616      * @param {Boolean} value True to allow the user to directly edit the field text
39617      */
39618     setEditable : function(value){
39619         if(value == this.editable){
39620             return;
39621         }
39622         this.editable = value;
39623         if(!value){
39624             this.el.dom.setAttribute('readOnly', true);
39625             this.el.on('mousedown', this.onTriggerClick,  this);
39626             this.el.addClass('x-combo-noedit');
39627         }else{
39628             this.el.dom.setAttribute('readOnly', false);
39629             this.el.un('mousedown', this.onTriggerClick,  this);
39630             this.el.removeClass('x-combo-noedit');
39631         }
39632     },
39633
39634     // private
39635     onBeforeLoad : function(){
39636         if(!this.hasFocus){
39637             return;
39638         }
39639         this.innerList.update(this.loadingText ?
39640                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39641         this.restrictHeight();
39642         this.selectedIndex = -1;
39643     },
39644
39645     // private
39646     onLoad : function(){
39647         if(!this.hasFocus){
39648             return;
39649         }
39650         if(this.store.getCount() > 0){
39651             this.expand();
39652             this.restrictHeight();
39653             if(this.lastQuery == this.allQuery){
39654                 if(this.editable){
39655                     this.el.dom.select();
39656                 }
39657                 if(!this.selectByValue(this.value, true)){
39658                     this.select(0, true);
39659                 }
39660             }else{
39661                 this.selectNext();
39662                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39663                     this.taTask.delay(this.typeAheadDelay);
39664                 }
39665             }
39666         }else{
39667             this.onEmptyResults();
39668         }
39669         //this.el.focus();
39670     },
39671     // private
39672     onLoadException : function()
39673     {
39674         this.collapse();
39675         Roo.log(this.store.reader.jsonData);
39676         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39677             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39678         }
39679         
39680         
39681     },
39682     // private
39683     onTypeAhead : function(){
39684         if(this.store.getCount() > 0){
39685             var r = this.store.getAt(0);
39686             var newValue = r.data[this.displayField];
39687             var len = newValue.length;
39688             var selStart = this.getRawValue().length;
39689             if(selStart != len){
39690                 this.setRawValue(newValue);
39691                 this.selectText(selStart, newValue.length);
39692             }
39693         }
39694     },
39695
39696     // private
39697     onSelect : function(record, index){
39698         if(this.fireEvent('beforeselect', this, record, index) !== false){
39699             this.setFromData(index > -1 ? record.data : false);
39700             this.collapse();
39701             this.fireEvent('select', this, record, index);
39702         }
39703     },
39704
39705     /**
39706      * Returns the currently selected field value or empty string if no value is set.
39707      * @return {String} value The selected value
39708      */
39709     getValue : function(){
39710         if(this.valueField){
39711             return typeof this.value != 'undefined' ? this.value : '';
39712         }else{
39713             return Roo.form.ComboBox.superclass.getValue.call(this);
39714         }
39715     },
39716
39717     /**
39718      * Clears any text/value currently set in the field
39719      */
39720     clearValue : function(){
39721         if(this.hiddenField){
39722             this.hiddenField.value = '';
39723         }
39724         this.value = '';
39725         this.setRawValue('');
39726         this.lastSelectionText = '';
39727         
39728     },
39729
39730     /**
39731      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39732      * will be displayed in the field.  If the value does not match the data value of an existing item,
39733      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39734      * Otherwise the field will be blank (although the value will still be set).
39735      * @param {String} value The value to match
39736      */
39737     setValue : function(v){
39738         var text = v;
39739         if(this.valueField){
39740             var r = this.findRecord(this.valueField, v);
39741             if(r){
39742                 text = r.data[this.displayField];
39743             }else if(this.valueNotFoundText !== undefined){
39744                 text = this.valueNotFoundText;
39745             }
39746         }
39747         this.lastSelectionText = text;
39748         if(this.hiddenField){
39749             this.hiddenField.value = v;
39750         }
39751         Roo.form.ComboBox.superclass.setValue.call(this, text);
39752         this.value = v;
39753     },
39754     /**
39755      * @property {Object} the last set data for the element
39756      */
39757     
39758     lastData : false,
39759     /**
39760      * Sets the value of the field based on a object which is related to the record format for the store.
39761      * @param {Object} value the value to set as. or false on reset?
39762      */
39763     setFromData : function(o){
39764         var dv = ''; // display value
39765         var vv = ''; // value value..
39766         this.lastData = o;
39767         if (this.displayField) {
39768             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39769         } else {
39770             // this is an error condition!!!
39771             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39772         }
39773         
39774         if(this.valueField){
39775             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39776         }
39777         if(this.hiddenField){
39778             this.hiddenField.value = vv;
39779             
39780             this.lastSelectionText = dv;
39781             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39782             this.value = vv;
39783             return;
39784         }
39785         // no hidden field.. - we store the value in 'value', but still display
39786         // display field!!!!
39787         this.lastSelectionText = dv;
39788         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39789         this.value = vv;
39790         
39791         
39792     },
39793     // private
39794     reset : function(){
39795         // overridden so that last data is reset..
39796         this.setValue(this.resetValue);
39797         this.clearInvalid();
39798         this.lastData = false;
39799         if (this.view) {
39800             this.view.clearSelections();
39801         }
39802     },
39803     // private
39804     findRecord : function(prop, value){
39805         var record;
39806         if(this.store.getCount() > 0){
39807             this.store.each(function(r){
39808                 if(r.data[prop] == value){
39809                     record = r;
39810                     return false;
39811                 }
39812                 return true;
39813             });
39814         }
39815         return record;
39816     },
39817     
39818     getName: function()
39819     {
39820         // returns hidden if it's set..
39821         if (!this.rendered) {return ''};
39822         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39823         
39824     },
39825     // private
39826     onViewMove : function(e, t){
39827         this.inKeyMode = false;
39828     },
39829
39830     // private
39831     onViewOver : function(e, t){
39832         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39833             return;
39834         }
39835         var item = this.view.findItemFromChild(t);
39836         if(item){
39837             var index = this.view.indexOf(item);
39838             this.select(index, false);
39839         }
39840     },
39841
39842     // private
39843     onViewClick : function(doFocus)
39844     {
39845         var index = this.view.getSelectedIndexes()[0];
39846         var r = this.store.getAt(index);
39847         if(r){
39848             this.onSelect(r, index);
39849         }
39850         if(doFocus !== false && !this.blockFocus){
39851             this.el.focus();
39852         }
39853     },
39854
39855     // private
39856     restrictHeight : function(){
39857         this.innerList.dom.style.height = '';
39858         var inner = this.innerList.dom;
39859         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39860         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39861         this.list.beginUpdate();
39862         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39863         this.list.alignTo(this.el, this.listAlign);
39864         this.list.endUpdate();
39865     },
39866
39867     // private
39868     onEmptyResults : function(){
39869         this.collapse();
39870     },
39871
39872     /**
39873      * Returns true if the dropdown list is expanded, else false.
39874      */
39875     isExpanded : function(){
39876         return this.list.isVisible();
39877     },
39878
39879     /**
39880      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39881      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39882      * @param {String} value The data value of the item to select
39883      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39884      * selected item if it is not currently in view (defaults to true)
39885      * @return {Boolean} True if the value matched an item in the list, else false
39886      */
39887     selectByValue : function(v, scrollIntoView){
39888         if(v !== undefined && v !== null){
39889             var r = this.findRecord(this.valueField || this.displayField, v);
39890             if(r){
39891                 this.select(this.store.indexOf(r), scrollIntoView);
39892                 return true;
39893             }
39894         }
39895         return false;
39896     },
39897
39898     /**
39899      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39900      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39901      * @param {Number} index The zero-based index of the list item to select
39902      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39903      * selected item if it is not currently in view (defaults to true)
39904      */
39905     select : function(index, scrollIntoView){
39906         this.selectedIndex = index;
39907         this.view.select(index);
39908         if(scrollIntoView !== false){
39909             var el = this.view.getNode(index);
39910             if(el){
39911                 this.innerList.scrollChildIntoView(el, false);
39912             }
39913         }
39914     },
39915
39916     // private
39917     selectNext : function(){
39918         var ct = this.store.getCount();
39919         if(ct > 0){
39920             if(this.selectedIndex == -1){
39921                 this.select(0);
39922             }else if(this.selectedIndex < ct-1){
39923                 this.select(this.selectedIndex+1);
39924             }
39925         }
39926     },
39927
39928     // private
39929     selectPrev : function(){
39930         var ct = this.store.getCount();
39931         if(ct > 0){
39932             if(this.selectedIndex == -1){
39933                 this.select(0);
39934             }else if(this.selectedIndex != 0){
39935                 this.select(this.selectedIndex-1);
39936             }
39937         }
39938     },
39939
39940     // private
39941     onKeyUp : function(e){
39942         if(this.editable !== false && !e.isSpecialKey()){
39943             this.lastKey = e.getKey();
39944             this.dqTask.delay(this.queryDelay);
39945         }
39946     },
39947
39948     // private
39949     validateBlur : function(){
39950         return !this.list || !this.list.isVisible();   
39951     },
39952
39953     // private
39954     initQuery : function(){
39955         this.doQuery(this.getRawValue());
39956     },
39957
39958     // private
39959     doForce : function(){
39960         if(this.el.dom.value.length > 0){
39961             this.el.dom.value =
39962                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39963              
39964         }
39965     },
39966
39967     /**
39968      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39969      * query allowing the query action to be canceled if needed.
39970      * @param {String} query The SQL query to execute
39971      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39972      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39973      * saved in the current store (defaults to false)
39974      */
39975     doQuery : function(q, forceAll){
39976         if(q === undefined || q === null){
39977             q = '';
39978         }
39979         var qe = {
39980             query: q,
39981             forceAll: forceAll,
39982             combo: this,
39983             cancel:false
39984         };
39985         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39986             return false;
39987         }
39988         q = qe.query;
39989         forceAll = qe.forceAll;
39990         if(forceAll === true || (q.length >= this.minChars)){
39991             if(this.lastQuery != q || this.alwaysQuery){
39992                 this.lastQuery = q;
39993                 if(this.mode == 'local'){
39994                     this.selectedIndex = -1;
39995                     if(forceAll){
39996                         this.store.clearFilter();
39997                     }else{
39998                         this.store.filter(this.displayField, q);
39999                     }
40000                     this.onLoad();
40001                 }else{
40002                     this.store.baseParams[this.queryParam] = q;
40003                     this.store.load({
40004                         params: this.getParams(q)
40005                     });
40006                     this.expand();
40007                 }
40008             }else{
40009                 this.selectedIndex = -1;
40010                 this.onLoad();   
40011             }
40012         }
40013     },
40014
40015     // private
40016     getParams : function(q){
40017         var p = {};
40018         //p[this.queryParam] = q;
40019         if(this.pageSize){
40020             p.start = 0;
40021             p.limit = this.pageSize;
40022         }
40023         return p;
40024     },
40025
40026     /**
40027      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40028      */
40029     collapse : function(){
40030         if(!this.isExpanded()){
40031             return;
40032         }
40033         this.list.hide();
40034         Roo.get(document).un('mousedown', this.collapseIf, this);
40035         Roo.get(document).un('mousewheel', this.collapseIf, this);
40036         if (!this.editable) {
40037             Roo.get(document).un('keydown', this.listKeyPress, this);
40038         }
40039         this.fireEvent('collapse', this);
40040     },
40041
40042     // private
40043     collapseIf : function(e){
40044         if(!e.within(this.wrap) && !e.within(this.list)){
40045             this.collapse();
40046         }
40047     },
40048
40049     /**
40050      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40051      */
40052     expand : function(){
40053         if(this.isExpanded() || !this.hasFocus){
40054             return;
40055         }
40056         this.list.alignTo(this.el, this.listAlign);
40057         this.list.show();
40058         Roo.get(document).on('mousedown', this.collapseIf, this);
40059         Roo.get(document).on('mousewheel', this.collapseIf, this);
40060         if (!this.editable) {
40061             Roo.get(document).on('keydown', this.listKeyPress, this);
40062         }
40063         
40064         this.fireEvent('expand', this);
40065     },
40066
40067     // private
40068     // Implements the default empty TriggerField.onTriggerClick function
40069     onTriggerClick : function(){
40070         if(this.disabled){
40071             return;
40072         }
40073         if(this.isExpanded()){
40074             this.collapse();
40075             if (!this.blockFocus) {
40076                 this.el.focus();
40077             }
40078             
40079         }else {
40080             this.hasFocus = true;
40081             if(this.triggerAction == 'all') {
40082                 this.doQuery(this.allQuery, true);
40083             } else {
40084                 this.doQuery(this.getRawValue());
40085             }
40086             if (!this.blockFocus) {
40087                 this.el.focus();
40088             }
40089         }
40090     },
40091     listKeyPress : function(e)
40092     {
40093         //Roo.log('listkeypress');
40094         // scroll to first matching element based on key pres..
40095         if (e.isSpecialKey()) {
40096             return false;
40097         }
40098         var k = String.fromCharCode(e.getKey()).toUpperCase();
40099         //Roo.log(k);
40100         var match  = false;
40101         var csel = this.view.getSelectedNodes();
40102         var cselitem = false;
40103         if (csel.length) {
40104             var ix = this.view.indexOf(csel[0]);
40105             cselitem  = this.store.getAt(ix);
40106             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40107                 cselitem = false;
40108             }
40109             
40110         }
40111         
40112         this.store.each(function(v) { 
40113             if (cselitem) {
40114                 // start at existing selection.
40115                 if (cselitem.id == v.id) {
40116                     cselitem = false;
40117                 }
40118                 return;
40119             }
40120                 
40121             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40122                 match = this.store.indexOf(v);
40123                 return false;
40124             }
40125         }, this);
40126         
40127         if (match === false) {
40128             return true; // no more action?
40129         }
40130         // scroll to?
40131         this.view.select(match);
40132         var sn = Roo.get(this.view.getSelectedNodes()[0])
40133         sn.scrollIntoView(sn.dom.parentNode, false);
40134     }
40135
40136     /** 
40137     * @cfg {Boolean} grow 
40138     * @hide 
40139     */
40140     /** 
40141     * @cfg {Number} growMin 
40142     * @hide 
40143     */
40144     /** 
40145     * @cfg {Number} growMax 
40146     * @hide 
40147     */
40148     /**
40149      * @hide
40150      * @method autoSize
40151      */
40152 });/*
40153  * Copyright(c) 2010-2012, Roo J Solutions Limited
40154  *
40155  * Licence LGPL
40156  *
40157  */
40158
40159 /**
40160  * @class Roo.form.ComboBoxArray
40161  * @extends Roo.form.TextField
40162  * A facebook style adder... for lists of email / people / countries  etc...
40163  * pick multiple items from a combo box, and shows each one.
40164  *
40165  *  Fred [x]  Brian [x]  [Pick another |v]
40166  *
40167  *
40168  *  For this to work: it needs various extra information
40169  *    - normal combo problay has
40170  *      name, hiddenName
40171  *    + displayField, valueField
40172  *
40173  *    For our purpose...
40174  *
40175  *
40176  *   If we change from 'extends' to wrapping...
40177  *   
40178  *  
40179  *
40180  
40181  
40182  * @constructor
40183  * Create a new ComboBoxArray.
40184  * @param {Object} config Configuration options
40185  */
40186  
40187
40188 Roo.form.ComboBoxArray = function(config)
40189 {
40190     this.addEvents({
40191         /**
40192          * @event remove
40193          * Fires when remove the value from the list
40194              * @param {Roo.form.ComboBoxArray} _self This combo box array
40195              * @param {Roo.form.ComboBoxArray.Item} item removed item
40196              */
40197         'remove' : true
40198         
40199         
40200     });
40201     
40202     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40203     
40204     this.items = new Roo.util.MixedCollection(false);
40205     
40206     // construct the child combo...
40207     
40208     
40209     
40210     
40211    
40212     
40213 }
40214
40215  
40216 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40217
40218     /**
40219      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40220      */
40221     
40222     lastData : false,
40223     
40224     // behavies liek a hiddne field
40225     inputType:      'hidden',
40226     /**
40227      * @cfg {Number} width The width of the box that displays the selected element
40228      */ 
40229     width:          300,
40230
40231     
40232     
40233     /**
40234      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40235      */
40236     name : false,
40237     /**
40238      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40239      */
40240     hiddenName : false,
40241     
40242     
40243     // private the array of items that are displayed..
40244     items  : false,
40245     // private - the hidden field el.
40246     hiddenEl : false,
40247     // private - the filed el..
40248     el : false,
40249     
40250     //validateValue : function() { return true; }, // all values are ok!
40251     //onAddClick: function() { },
40252     
40253     onRender : function(ct, position) 
40254     {
40255         
40256         // create the standard hidden element
40257         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40258         
40259         
40260         // give fake names to child combo;
40261         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40262         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40263         
40264         this.combo = Roo.factory(this.combo, Roo.form);
40265         this.combo.onRender(ct, position);
40266         if (typeof(this.combo.width) != 'undefined') {
40267             this.combo.onResize(this.combo.width,0);
40268         }
40269         
40270         this.combo.initEvents();
40271         
40272         // assigned so form know we need to do this..
40273         this.store          = this.combo.store;
40274         this.valueField     = this.combo.valueField;
40275         this.displayField   = this.combo.displayField ;
40276         
40277         
40278         this.combo.wrap.addClass('x-cbarray-grp');
40279         
40280         var cbwrap = this.combo.wrap.createChild(
40281             {tag: 'div', cls: 'x-cbarray-cb'},
40282             this.combo.el.dom
40283         );
40284         
40285              
40286         this.hiddenEl = this.combo.wrap.createChild({
40287             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40288         });
40289         this.el = this.combo.wrap.createChild({
40290             tag: 'input',  type:'hidden' , name: this.name, value : ''
40291         });
40292          //   this.el.dom.removeAttribute("name");
40293         
40294         
40295         this.outerWrap = this.combo.wrap;
40296         this.wrap = cbwrap;
40297         
40298         this.outerWrap.setWidth(this.width);
40299         this.outerWrap.dom.removeChild(this.el.dom);
40300         
40301         this.wrap.dom.appendChild(this.el.dom);
40302         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40303         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40304         
40305         this.combo.trigger.setStyle('position','relative');
40306         this.combo.trigger.setStyle('left', '0px');
40307         this.combo.trigger.setStyle('top', '2px');
40308         
40309         this.combo.el.setStyle('vertical-align', 'text-bottom');
40310         
40311         //this.trigger.setStyle('vertical-align', 'top');
40312         
40313         // this should use the code from combo really... on('add' ....)
40314         if (this.adder) {
40315             
40316         
40317             this.adder = this.outerWrap.createChild(
40318                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40319             var _t = this;
40320             this.adder.on('click', function(e) {
40321                 _t.fireEvent('adderclick', this, e);
40322             }, _t);
40323         }
40324         //var _t = this;
40325         //this.adder.on('click', this.onAddClick, _t);
40326         
40327         
40328         this.combo.on('select', function(cb, rec, ix) {
40329             this.addItem(rec.data);
40330             
40331             cb.setValue('');
40332             cb.el.dom.value = '';
40333             //cb.lastData = rec.data;
40334             // add to list
40335             
40336         }, this);
40337         
40338         
40339     },
40340     
40341     
40342     getName: function()
40343     {
40344         // returns hidden if it's set..
40345         if (!this.rendered) {return ''};
40346         return  this.hiddenName ? this.hiddenName : this.name;
40347         
40348     },
40349     
40350     
40351     onResize: function(w, h){
40352         
40353         return;
40354         // not sure if this is needed..
40355         //this.combo.onResize(w,h);
40356         
40357         if(typeof w != 'number'){
40358             // we do not handle it!?!?
40359             return;
40360         }
40361         var tw = this.combo.trigger.getWidth();
40362         tw += this.addicon ? this.addicon.getWidth() : 0;
40363         tw += this.editicon ? this.editicon.getWidth() : 0;
40364         var x = w - tw;
40365         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40366             
40367         this.combo.trigger.setStyle('left', '0px');
40368         
40369         if(this.list && this.listWidth === undefined){
40370             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40371             this.list.setWidth(lw);
40372             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40373         }
40374         
40375     
40376         
40377     },
40378     
40379     addItem: function(rec)
40380     {
40381         var valueField = this.combo.valueField;
40382         var displayField = this.combo.displayField;
40383         if (this.items.indexOfKey(rec[valueField]) > -1) {
40384             //console.log("GOT " + rec.data.id);
40385             return;
40386         }
40387         
40388         var x = new Roo.form.ComboBoxArray.Item({
40389             //id : rec[this.idField],
40390             data : rec,
40391             displayField : displayField ,
40392             tipField : displayField ,
40393             cb : this
40394         });
40395         // use the 
40396         this.items.add(rec[valueField],x);
40397         // add it before the element..
40398         this.updateHiddenEl();
40399         x.render(this.outerWrap, this.wrap.dom);
40400         // add the image handler..
40401     },
40402     
40403     updateHiddenEl : function()
40404     {
40405         this.validate();
40406         if (!this.hiddenEl) {
40407             return;
40408         }
40409         var ar = [];
40410         var idField = this.combo.valueField;
40411         
40412         this.items.each(function(f) {
40413             ar.push(f.data[idField]);
40414            
40415         });
40416         this.hiddenEl.dom.value = ar.join(',');
40417         this.validate();
40418     },
40419     
40420     reset : function()
40421     {
40422         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40423         this.items.each(function(f) {
40424            f.remove(); 
40425         });
40426         this.el.dom.value = '';
40427         if (this.hiddenEl) {
40428             this.hiddenEl.dom.value = '';
40429         }
40430         
40431     },
40432     getValue: function()
40433     {
40434         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40435     },
40436     setValue: function(v) // not a valid action - must use addItems..
40437     {
40438          
40439         this.reset();
40440         
40441         
40442         
40443         if (this.store.isLocal && (typeof(v) == 'string')) {
40444             // then we can use the store to find the values..
40445             // comma seperated at present.. this needs to allow JSON based encoding..
40446             this.hiddenEl.value  = v;
40447             var v_ar = [];
40448             Roo.each(v.split(','), function(k) {
40449                 Roo.log("CHECK " + this.valueField + ',' + k);
40450                 var li = this.store.query(this.valueField, k);
40451                 if (!li.length) {
40452                     return;
40453                 }
40454                 var add = {};
40455                 add[this.valueField] = k;
40456                 add[this.displayField] = li.item(0).data[this.displayField];
40457                 
40458                 this.addItem(add);
40459             }, this) 
40460              
40461         }
40462         if (typeof(v) == 'object') {
40463             // then let's assume it's an array of objects..
40464             Roo.each(v, function(l) {
40465                 this.addItem(l);
40466             }, this);
40467              
40468         }
40469         
40470         
40471     },
40472     setFromData: function(v)
40473     {
40474         // this recieves an object, if setValues is called.
40475         this.reset();
40476         this.el.dom.value = v[this.displayField];
40477         this.hiddenEl.dom.value = v[this.valueField];
40478         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40479             return;
40480         }
40481         var kv = v[this.valueField];
40482         var dv = v[this.displayField];
40483         kv = typeof(kv) != 'string' ? '' : kv;
40484         dv = typeof(dv) != 'string' ? '' : dv;
40485         
40486         
40487         var keys = kv.split(',');
40488         var display = dv.split(',');
40489         for (var i = 0 ; i < keys.length; i++) {
40490             
40491             add = {};
40492             add[this.valueField] = keys[i];
40493             add[this.displayField] = display[i];
40494             this.addItem(add);
40495         }
40496       
40497         
40498     },
40499     
40500     /**
40501      * Validates the combox array value
40502      * @return {Boolean} True if the value is valid, else false
40503      */
40504     validate : function(){
40505         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40506             this.clearInvalid();
40507             return true;
40508         }
40509         return false;
40510     },
40511     
40512     validateValue : function(value){
40513         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40514         
40515     },
40516     
40517     /*@
40518      * overide
40519      * 
40520      */
40521     isDirty : function() {
40522         if(this.disabled) {
40523             return false;
40524         }
40525         
40526         try {
40527             var d = Roo.decode(String(this.originalValue));
40528         } catch (e) {
40529             return String(this.getValue()) !== String(this.originalValue);
40530         }
40531         
40532         var originalValue = [];
40533         
40534         for (var i = 0; i < d.length; i++){
40535             originalValue.push(d[i][this.valueField]);
40536         }
40537         
40538         return String(this.getValue()) !== String(originalValue.join(','));
40539         
40540     }
40541     
40542 });
40543
40544
40545
40546 /**
40547  * @class Roo.form.ComboBoxArray.Item
40548  * @extends Roo.BoxComponent
40549  * A selected item in the list
40550  *  Fred [x]  Brian [x]  [Pick another |v]
40551  * 
40552  * @constructor
40553  * Create a new item.
40554  * @param {Object} config Configuration options
40555  */
40556  
40557 Roo.form.ComboBoxArray.Item = function(config) {
40558     config.id = Roo.id();
40559     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40560 }
40561
40562 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40563     data : {},
40564     cb: false,
40565     displayField : false,
40566     tipField : false,
40567     
40568     
40569     defaultAutoCreate : {
40570         tag: 'div',
40571         cls: 'x-cbarray-item',
40572         cn : [ 
40573             { tag: 'div' },
40574             {
40575                 tag: 'img',
40576                 width:16,
40577                 height : 16,
40578                 src : Roo.BLANK_IMAGE_URL ,
40579                 align: 'center'
40580             }
40581         ]
40582         
40583     },
40584     
40585  
40586     onRender : function(ct, position)
40587     {
40588         Roo.form.Field.superclass.onRender.call(this, ct, position);
40589         
40590         if(!this.el){
40591             var cfg = this.getAutoCreate();
40592             this.el = ct.createChild(cfg, position);
40593         }
40594         
40595         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40596         
40597         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40598             this.cb.renderer(this.data) :
40599             String.format('{0}',this.data[this.displayField]);
40600         
40601             
40602         this.el.child('div').dom.setAttribute('qtip',
40603                         String.format('{0}',this.data[this.tipField])
40604         );
40605         
40606         this.el.child('img').on('click', this.remove, this);
40607         
40608     },
40609    
40610     remove : function()
40611     {
40612         this.cb.items.remove(this);
40613         this.el.child('img').un('click', this.remove, this);
40614         this.el.remove();
40615         this.cb.updateHiddenEl();
40616         
40617         this.cb.fireEvent('remove', this.cb, this);
40618     }
40619 });/*
40620  * Based on:
40621  * Ext JS Library 1.1.1
40622  * Copyright(c) 2006-2007, Ext JS, LLC.
40623  *
40624  * Originally Released Under LGPL - original licence link has changed is not relivant.
40625  *
40626  * Fork - LGPL
40627  * <script type="text/javascript">
40628  */
40629 /**
40630  * @class Roo.form.Checkbox
40631  * @extends Roo.form.Field
40632  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40633  * @constructor
40634  * Creates a new Checkbox
40635  * @param {Object} config Configuration options
40636  */
40637 Roo.form.Checkbox = function(config){
40638     Roo.form.Checkbox.superclass.constructor.call(this, config);
40639     this.addEvents({
40640         /**
40641          * @event check
40642          * Fires when the checkbox is checked or unchecked.
40643              * @param {Roo.form.Checkbox} this This checkbox
40644              * @param {Boolean} checked The new checked value
40645              */
40646         check : true
40647     });
40648 };
40649
40650 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40651     /**
40652      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40653      */
40654     focusClass : undefined,
40655     /**
40656      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40657      */
40658     fieldClass: "x-form-field",
40659     /**
40660      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40661      */
40662     checked: false,
40663     /**
40664      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40665      * {tag: "input", type: "checkbox", autocomplete: "off"})
40666      */
40667     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40668     /**
40669      * @cfg {String} boxLabel The text that appears beside the checkbox
40670      */
40671     boxLabel : "",
40672     /**
40673      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40674      */  
40675     inputValue : '1',
40676     /**
40677      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40678      */
40679      valueOff: '0', // value when not checked..
40680
40681     actionMode : 'viewEl', 
40682     //
40683     // private
40684     itemCls : 'x-menu-check-item x-form-item',
40685     groupClass : 'x-menu-group-item',
40686     inputType : 'hidden',
40687     
40688     
40689     inSetChecked: false, // check that we are not calling self...
40690     
40691     inputElement: false, // real input element?
40692     basedOn: false, // ????
40693     
40694     isFormField: true, // not sure where this is needed!!!!
40695
40696     onResize : function(){
40697         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40698         if(!this.boxLabel){
40699             this.el.alignTo(this.wrap, 'c-c');
40700         }
40701     },
40702
40703     initEvents : function(){
40704         Roo.form.Checkbox.superclass.initEvents.call(this);
40705         this.el.on("click", this.onClick,  this);
40706         this.el.on("change", this.onClick,  this);
40707     },
40708
40709
40710     getResizeEl : function(){
40711         return this.wrap;
40712     },
40713
40714     getPositionEl : function(){
40715         return this.wrap;
40716     },
40717
40718     // private
40719     onRender : function(ct, position){
40720         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40721         /*
40722         if(this.inputValue !== undefined){
40723             this.el.dom.value = this.inputValue;
40724         }
40725         */
40726         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40727         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40728         var viewEl = this.wrap.createChild({ 
40729             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40730         this.viewEl = viewEl;   
40731         this.wrap.on('click', this.onClick,  this); 
40732         
40733         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40734         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40735         
40736         
40737         
40738         if(this.boxLabel){
40739             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40740         //    viewEl.on('click', this.onClick,  this); 
40741         }
40742         //if(this.checked){
40743             this.setChecked(this.checked);
40744         //}else{
40745             //this.checked = this.el.dom;
40746         //}
40747
40748     },
40749
40750     // private
40751     initValue : Roo.emptyFn,
40752
40753     /**
40754      * Returns the checked state of the checkbox.
40755      * @return {Boolean} True if checked, else false
40756      */
40757     getValue : function(){
40758         if(this.el){
40759             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40760         }
40761         return this.valueOff;
40762         
40763     },
40764
40765         // private
40766     onClick : function(){ 
40767         this.setChecked(!this.checked);
40768
40769         //if(this.el.dom.checked != this.checked){
40770         //    this.setValue(this.el.dom.checked);
40771        // }
40772     },
40773
40774     /**
40775      * Sets the checked state of the checkbox.
40776      * On is always based on a string comparison between inputValue and the param.
40777      * @param {Boolean/String} value - the value to set 
40778      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40779      */
40780     setValue : function(v,suppressEvent){
40781         
40782         
40783         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40784         //if(this.el && this.el.dom){
40785         //    this.el.dom.checked = this.checked;
40786         //    this.el.dom.defaultChecked = this.checked;
40787         //}
40788         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40789         //this.fireEvent("check", this, this.checked);
40790     },
40791     // private..
40792     setChecked : function(state,suppressEvent)
40793     {
40794         if (this.inSetChecked) {
40795             this.checked = state;
40796             return;
40797         }
40798         
40799     
40800         if(this.wrap){
40801             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40802         }
40803         this.checked = state;
40804         if(suppressEvent !== true){
40805             this.fireEvent('check', this, state);
40806         }
40807         this.inSetChecked = true;
40808         this.el.dom.value = state ? this.inputValue : this.valueOff;
40809         this.inSetChecked = false;
40810         
40811     },
40812     // handle setting of hidden value by some other method!!?!?
40813     setFromHidden: function()
40814     {
40815         if(!this.el){
40816             return;
40817         }
40818         //console.log("SET FROM HIDDEN");
40819         //alert('setFrom hidden');
40820         this.setValue(this.el.dom.value);
40821     },
40822     
40823     onDestroy : function()
40824     {
40825         if(this.viewEl){
40826             Roo.get(this.viewEl).remove();
40827         }
40828          
40829         Roo.form.Checkbox.superclass.onDestroy.call(this);
40830     }
40831
40832 });/*
40833  * Based on:
40834  * Ext JS Library 1.1.1
40835  * Copyright(c) 2006-2007, Ext JS, LLC.
40836  *
40837  * Originally Released Under LGPL - original licence link has changed is not relivant.
40838  *
40839  * Fork - LGPL
40840  * <script type="text/javascript">
40841  */
40842  
40843 /**
40844  * @class Roo.form.Radio
40845  * @extends Roo.form.Checkbox
40846  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40847  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40848  * @constructor
40849  * Creates a new Radio
40850  * @param {Object} config Configuration options
40851  */
40852 Roo.form.Radio = function(){
40853     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40854 };
40855 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40856     inputType: 'radio',
40857
40858     /**
40859      * If this radio is part of a group, it will return the selected value
40860      * @return {String}
40861      */
40862     getGroupValue : function(){
40863         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40864     },
40865     
40866     
40867     onRender : function(ct, position){
40868         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40869         
40870         if(this.inputValue !== undefined){
40871             this.el.dom.value = this.inputValue;
40872         }
40873          
40874         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40875         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40876         //var viewEl = this.wrap.createChild({ 
40877         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40878         //this.viewEl = viewEl;   
40879         //this.wrap.on('click', this.onClick,  this); 
40880         
40881         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40882         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40883         
40884         
40885         
40886         if(this.boxLabel){
40887             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40888         //    viewEl.on('click', this.onClick,  this); 
40889         }
40890          if(this.checked){
40891             this.el.dom.checked =   'checked' ;
40892         }
40893          
40894     } 
40895     
40896     
40897 });//<script type="text/javascript">
40898
40899 /*
40900  * Based  Ext JS Library 1.1.1
40901  * Copyright(c) 2006-2007, Ext JS, LLC.
40902  * LGPL
40903  *
40904  */
40905  
40906 /**
40907  * @class Roo.HtmlEditorCore
40908  * @extends Roo.Component
40909  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40910  *
40911  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40912  */
40913
40914 Roo.HtmlEditorCore = function(config){
40915     
40916     
40917     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40918     this.addEvents({
40919         /**
40920          * @event initialize
40921          * Fires when the editor is fully initialized (including the iframe)
40922          * @param {Roo.HtmlEditorCore} this
40923          */
40924         initialize: true,
40925         /**
40926          * @event activate
40927          * Fires when the editor is first receives the focus. Any insertion must wait
40928          * until after this event.
40929          * @param {Roo.HtmlEditorCore} this
40930          */
40931         activate: true,
40932          /**
40933          * @event beforesync
40934          * Fires before the textarea is updated with content from the editor iframe. Return false
40935          * to cancel the sync.
40936          * @param {Roo.HtmlEditorCore} this
40937          * @param {String} html
40938          */
40939         beforesync: true,
40940          /**
40941          * @event beforepush
40942          * Fires before the iframe editor is updated with content from the textarea. Return false
40943          * to cancel the push.
40944          * @param {Roo.HtmlEditorCore} this
40945          * @param {String} html
40946          */
40947         beforepush: true,
40948          /**
40949          * @event sync
40950          * Fires when the textarea is updated with content from the editor iframe.
40951          * @param {Roo.HtmlEditorCore} this
40952          * @param {String} html
40953          */
40954         sync: true,
40955          /**
40956          * @event push
40957          * Fires when the iframe editor is updated with content from the textarea.
40958          * @param {Roo.HtmlEditorCore} this
40959          * @param {String} html
40960          */
40961         push: true,
40962         
40963         /**
40964          * @event editorevent
40965          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40966          * @param {Roo.HtmlEditorCore} this
40967          */
40968         editorevent: true
40969     });
40970      
40971 };
40972
40973
40974 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
40975
40976
40977      /**
40978      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
40979      */
40980     
40981     owner : false,
40982     
40983      /**
40984      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40985      *                        Roo.resizable.
40986      */
40987     resizable : false,
40988      /**
40989      * @cfg {Number} height (in pixels)
40990      */   
40991     height: 300,
40992    /**
40993      * @cfg {Number} width (in pixels)
40994      */   
40995     width: 500,
40996     
40997     /**
40998      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40999      * 
41000      */
41001     stylesheets: false,
41002     
41003     // id of frame..
41004     frameId: false,
41005     
41006     // private properties
41007     validationEvent : false,
41008     deferHeight: true,
41009     initialized : false,
41010     activated : false,
41011     sourceEditMode : false,
41012     onFocus : Roo.emptyFn,
41013     iframePad:3,
41014     hideMode:'offsets',
41015     
41016     clearUp: true,
41017     
41018      
41019     
41020
41021     /**
41022      * Protected method that will not generally be called directly. It
41023      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41024      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41025      */
41026     getDocMarkup : function(){
41027         // body styles..
41028         var st = '';
41029         Roo.log(this.stylesheets);
41030         
41031         // inherit styels from page...?? 
41032         if (this.stylesheets === false) {
41033             
41034             Roo.get(document.head).select('style').each(function(node) {
41035                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41036             });
41037             
41038             Roo.get(document.head).select('link').each(function(node) { 
41039                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41040             });
41041             
41042         } else if (!this.stylesheets.length) {
41043                 // simple..
41044                 st = '<style type="text/css">' +
41045                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41046                    '</style>';
41047         } else {
41048             Roo.each(this.stylesheets, function(s) {
41049                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41050             });
41051             
41052         }
41053         
41054         st +=  '<style type="text/css">' +
41055             'IMG { cursor: pointer } ' +
41056         '</style>';
41057
41058         
41059         return '<html><head>' + st  +
41060             //<style type="text/css">' +
41061             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41062             //'</style>' +
41063             ' </head><body class="roo-htmleditor-body"></body></html>';
41064     },
41065
41066     // private
41067     onRender : function(ct, position)
41068     {
41069         var _t = this;
41070         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41071         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41072         
41073         
41074         this.el.dom.style.border = '0 none';
41075         this.el.dom.setAttribute('tabIndex', -1);
41076         this.el.addClass('x-hidden hide');
41077         
41078         
41079         
41080         if(Roo.isIE){ // fix IE 1px bogus margin
41081             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41082         }
41083        
41084         
41085         this.frameId = Roo.id();
41086         
41087          
41088         
41089         var iframe = this.owner.wrap.createChild({
41090             tag: 'iframe',
41091             cls: 'form-control', // bootstrap..
41092             id: this.frameId,
41093             name: this.frameId,
41094             frameBorder : 'no',
41095             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41096         }, this.el
41097         );
41098         
41099         
41100         this.iframe = iframe.dom;
41101
41102          this.assignDocWin();
41103         
41104         this.doc.designMode = 'on';
41105        
41106         this.doc.open();
41107         this.doc.write(this.getDocMarkup());
41108         this.doc.close();
41109
41110         
41111         var task = { // must defer to wait for browser to be ready
41112             run : function(){
41113                 //console.log("run task?" + this.doc.readyState);
41114                 this.assignDocWin();
41115                 if(this.doc.body || this.doc.readyState == 'complete'){
41116                     try {
41117                         this.doc.designMode="on";
41118                     } catch (e) {
41119                         return;
41120                     }
41121                     Roo.TaskMgr.stop(task);
41122                     this.initEditor.defer(10, this);
41123                 }
41124             },
41125             interval : 10,
41126             duration: 10000,
41127             scope: this
41128         };
41129         Roo.TaskMgr.start(task);
41130
41131         
41132          
41133     },
41134
41135     // private
41136     onResize : function(w, h)
41137     {
41138          Roo.log('resize: ' +w + ',' + h );
41139         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41140         if(!this.iframe){
41141             return;
41142         }
41143         if(typeof w == 'number'){
41144             
41145             this.iframe.style.width = w + 'px';
41146         }
41147         if(typeof h == 'number'){
41148             
41149             this.iframe.style.height = h + 'px';
41150             if(this.doc){
41151                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41152             }
41153         }
41154         
41155     },
41156
41157     /**
41158      * Toggles the editor between standard and source edit mode.
41159      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41160      */
41161     toggleSourceEdit : function(sourceEditMode){
41162         
41163         this.sourceEditMode = sourceEditMode === true;
41164         
41165         if(this.sourceEditMode){
41166  
41167             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41168             
41169         }else{
41170             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41171             //this.iframe.className = '';
41172             this.deferFocus();
41173         }
41174         //this.setSize(this.owner.wrap.getSize());
41175         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41176     },
41177
41178     
41179   
41180
41181     /**
41182      * Protected method that will not generally be called directly. If you need/want
41183      * custom HTML cleanup, this is the method you should override.
41184      * @param {String} html The HTML to be cleaned
41185      * return {String} The cleaned HTML
41186      */
41187     cleanHtml : function(html){
41188         html = String(html);
41189         if(html.length > 5){
41190             if(Roo.isSafari){ // strip safari nonsense
41191                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41192             }
41193         }
41194         if(html == '&nbsp;'){
41195             html = '';
41196         }
41197         return html;
41198     },
41199
41200     /**
41201      * HTML Editor -> Textarea
41202      * Protected method that will not generally be called directly. Syncs the contents
41203      * of the editor iframe with the textarea.
41204      */
41205     syncValue : function(){
41206         if(this.initialized){
41207             var bd = (this.doc.body || this.doc.documentElement);
41208             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41209             var html = bd.innerHTML;
41210             if(Roo.isSafari){
41211                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41212                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41213                 if(m && m[1]){
41214                     html = '<div style="'+m[0]+'">' + html + '</div>';
41215                 }
41216             }
41217             html = this.cleanHtml(html);
41218             // fix up the special chars.. normaly like back quotes in word...
41219             // however we do not want to do this with chinese..
41220             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41221                 var cc = b.charCodeAt();
41222                 if (
41223                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41224                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41225                     (cc >= 0xf900 && cc < 0xfb00 )
41226                 ) {
41227                         return b;
41228                 }
41229                 return "&#"+cc+";" 
41230             });
41231             if(this.owner.fireEvent('beforesync', this, html) !== false){
41232                 this.el.dom.value = html;
41233                 this.owner.fireEvent('sync', this, html);
41234             }
41235         }
41236     },
41237
41238     /**
41239      * Protected method that will not generally be called directly. Pushes the value of the textarea
41240      * into the iframe editor.
41241      */
41242     pushValue : function(){
41243         if(this.initialized){
41244             var v = this.el.dom.value.trim();
41245             
41246 //            if(v.length < 1){
41247 //                v = '&#160;';
41248 //            }
41249             
41250             if(this.owner.fireEvent('beforepush', this, v) !== false){
41251                 var d = (this.doc.body || this.doc.documentElement);
41252                 d.innerHTML = v;
41253                 this.cleanUpPaste();
41254                 this.el.dom.value = d.innerHTML;
41255                 this.owner.fireEvent('push', this, v);
41256             }
41257         }
41258     },
41259
41260     // private
41261     deferFocus : function(){
41262         this.focus.defer(10, this);
41263     },
41264
41265     // doc'ed in Field
41266     focus : function(){
41267         if(this.win && !this.sourceEditMode){
41268             this.win.focus();
41269         }else{
41270             this.el.focus();
41271         }
41272     },
41273     
41274     assignDocWin: function()
41275     {
41276         var iframe = this.iframe;
41277         
41278          if(Roo.isIE){
41279             this.doc = iframe.contentWindow.document;
41280             this.win = iframe.contentWindow;
41281         } else {
41282             if (!Roo.get(this.frameId)) {
41283                 return;
41284             }
41285             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41286             this.win = Roo.get(this.frameId).dom.contentWindow;
41287         }
41288     },
41289     
41290     // private
41291     initEditor : function(){
41292         //console.log("INIT EDITOR");
41293         this.assignDocWin();
41294         
41295         
41296         
41297         this.doc.designMode="on";
41298         this.doc.open();
41299         this.doc.write(this.getDocMarkup());
41300         this.doc.close();
41301         
41302         var dbody = (this.doc.body || this.doc.documentElement);
41303         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41304         // this copies styles from the containing element into thsi one..
41305         // not sure why we need all of this..
41306         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41307         ss['background-attachment'] = 'fixed'; // w3c
41308         dbody.bgProperties = 'fixed'; // ie
41309         Roo.DomHelper.applyStyles(dbody, ss);
41310         Roo.EventManager.on(this.doc, {
41311             //'mousedown': this.onEditorEvent,
41312             'mouseup': this.onEditorEvent,
41313             'dblclick': this.onEditorEvent,
41314             'click': this.onEditorEvent,
41315             'keyup': this.onEditorEvent,
41316             buffer:100,
41317             scope: this
41318         });
41319         if(Roo.isGecko){
41320             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41321         }
41322         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41323             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41324         }
41325         this.initialized = true;
41326
41327         this.owner.fireEvent('initialize', this);
41328         this.pushValue();
41329     },
41330
41331     // private
41332     onDestroy : function(){
41333         
41334         
41335         
41336         if(this.rendered){
41337             
41338             //for (var i =0; i < this.toolbars.length;i++) {
41339             //    // fixme - ask toolbars for heights?
41340             //    this.toolbars[i].onDestroy();
41341            // }
41342             
41343             //this.wrap.dom.innerHTML = '';
41344             //this.wrap.remove();
41345         }
41346     },
41347
41348     // private
41349     onFirstFocus : function(){
41350         
41351         this.assignDocWin();
41352         
41353         
41354         this.activated = true;
41355          
41356     
41357         if(Roo.isGecko){ // prevent silly gecko errors
41358             this.win.focus();
41359             var s = this.win.getSelection();
41360             if(!s.focusNode || s.focusNode.nodeType != 3){
41361                 var r = s.getRangeAt(0);
41362                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41363                 r.collapse(true);
41364                 this.deferFocus();
41365             }
41366             try{
41367                 this.execCmd('useCSS', true);
41368                 this.execCmd('styleWithCSS', false);
41369             }catch(e){}
41370         }
41371         this.owner.fireEvent('activate', this);
41372     },
41373
41374     // private
41375     adjustFont: function(btn){
41376         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41377         //if(Roo.isSafari){ // safari
41378         //    adjust *= 2;
41379        // }
41380         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41381         if(Roo.isSafari){ // safari
41382             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41383             v =  (v < 10) ? 10 : v;
41384             v =  (v > 48) ? 48 : v;
41385             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41386             
41387         }
41388         
41389         
41390         v = Math.max(1, v+adjust);
41391         
41392         this.execCmd('FontSize', v  );
41393     },
41394
41395     onEditorEvent : function(e){
41396         this.owner.fireEvent('editorevent', this, e);
41397       //  this.updateToolbar();
41398         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41399     },
41400
41401     insertTag : function(tg)
41402     {
41403         // could be a bit smarter... -> wrap the current selected tRoo..
41404         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41405             
41406             range = this.createRange(this.getSelection());
41407             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41408             wrappingNode.appendChild(range.extractContents());
41409             range.insertNode(wrappingNode);
41410
41411             return;
41412             
41413             
41414             
41415         }
41416         this.execCmd("formatblock",   tg);
41417         
41418     },
41419     
41420     insertText : function(txt)
41421     {
41422         
41423         
41424         var range = this.createRange();
41425         range.deleteContents();
41426                //alert(Sender.getAttribute('label'));
41427                
41428         range.insertNode(this.doc.createTextNode(txt));
41429     } ,
41430     
41431      
41432
41433     /**
41434      * Executes a Midas editor command on the editor document and performs necessary focus and
41435      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41436      * @param {String} cmd The Midas command
41437      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41438      */
41439     relayCmd : function(cmd, value){
41440         this.win.focus();
41441         this.execCmd(cmd, value);
41442         this.owner.fireEvent('editorevent', this);
41443         //this.updateToolbar();
41444         this.owner.deferFocus();
41445     },
41446
41447     /**
41448      * Executes a Midas editor command directly on the editor document.
41449      * For visual commands, you should use {@link #relayCmd} instead.
41450      * <b>This should only be called after the editor is initialized.</b>
41451      * @param {String} cmd The Midas command
41452      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41453      */
41454     execCmd : function(cmd, value){
41455         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41456         this.syncValue();
41457     },
41458  
41459  
41460    
41461     /**
41462      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41463      * to insert tRoo.
41464      * @param {String} text | dom node.. 
41465      */
41466     insertAtCursor : function(text)
41467     {
41468         
41469         
41470         
41471         if(!this.activated){
41472             return;
41473         }
41474         /*
41475         if(Roo.isIE){
41476             this.win.focus();
41477             var r = this.doc.selection.createRange();
41478             if(r){
41479                 r.collapse(true);
41480                 r.pasteHTML(text);
41481                 this.syncValue();
41482                 this.deferFocus();
41483             
41484             }
41485             return;
41486         }
41487         */
41488         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41489             this.win.focus();
41490             
41491             
41492             // from jquery ui (MIT licenced)
41493             var range, node;
41494             var win = this.win;
41495             
41496             if (win.getSelection && win.getSelection().getRangeAt) {
41497                 range = win.getSelection().getRangeAt(0);
41498                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41499                 range.insertNode(node);
41500             } else if (win.document.selection && win.document.selection.createRange) {
41501                 // no firefox support
41502                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41503                 win.document.selection.createRange().pasteHTML(txt);
41504             } else {
41505                 // no firefox support
41506                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41507                 this.execCmd('InsertHTML', txt);
41508             } 
41509             
41510             this.syncValue();
41511             
41512             this.deferFocus();
41513         }
41514     },
41515  // private
41516     mozKeyPress : function(e){
41517         if(e.ctrlKey){
41518             var c = e.getCharCode(), cmd;
41519           
41520             if(c > 0){
41521                 c = String.fromCharCode(c).toLowerCase();
41522                 switch(c){
41523                     case 'b':
41524                         cmd = 'bold';
41525                         break;
41526                     case 'i':
41527                         cmd = 'italic';
41528                         break;
41529                     
41530                     case 'u':
41531                         cmd = 'underline';
41532                         break;
41533                     
41534                     case 'v':
41535                         this.cleanUpPaste.defer(100, this);
41536                         return;
41537                         
41538                 }
41539                 if(cmd){
41540                     this.win.focus();
41541                     this.execCmd(cmd);
41542                     this.deferFocus();
41543                     e.preventDefault();
41544                 }
41545                 
41546             }
41547         }
41548     },
41549
41550     // private
41551     fixKeys : function(){ // load time branching for fastest keydown performance
41552         if(Roo.isIE){
41553             return function(e){
41554                 var k = e.getKey(), r;
41555                 if(k == e.TAB){
41556                     e.stopEvent();
41557                     r = this.doc.selection.createRange();
41558                     if(r){
41559                         r.collapse(true);
41560                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41561                         this.deferFocus();
41562                     }
41563                     return;
41564                 }
41565                 
41566                 if(k == e.ENTER){
41567                     r = this.doc.selection.createRange();
41568                     if(r){
41569                         var target = r.parentElement();
41570                         if(!target || target.tagName.toLowerCase() != 'li'){
41571                             e.stopEvent();
41572                             r.pasteHTML('<br />');
41573                             r.collapse(false);
41574                             r.select();
41575                         }
41576                     }
41577                 }
41578                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41579                     this.cleanUpPaste.defer(100, this);
41580                     return;
41581                 }
41582                 
41583                 
41584             };
41585         }else if(Roo.isOpera){
41586             return function(e){
41587                 var k = e.getKey();
41588                 if(k == e.TAB){
41589                     e.stopEvent();
41590                     this.win.focus();
41591                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41592                     this.deferFocus();
41593                 }
41594                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41595                     this.cleanUpPaste.defer(100, this);
41596                     return;
41597                 }
41598                 
41599             };
41600         }else if(Roo.isSafari){
41601             return function(e){
41602                 var k = e.getKey();
41603                 
41604                 if(k == e.TAB){
41605                     e.stopEvent();
41606                     this.execCmd('InsertText','\t');
41607                     this.deferFocus();
41608                     return;
41609                 }
41610                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41611                     this.cleanUpPaste.defer(100, this);
41612                     return;
41613                 }
41614                 
41615              };
41616         }
41617     }(),
41618     
41619     getAllAncestors: function()
41620     {
41621         var p = this.getSelectedNode();
41622         var a = [];
41623         if (!p) {
41624             a.push(p); // push blank onto stack..
41625             p = this.getParentElement();
41626         }
41627         
41628         
41629         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41630             a.push(p);
41631             p = p.parentNode;
41632         }
41633         a.push(this.doc.body);
41634         return a;
41635     },
41636     lastSel : false,
41637     lastSelNode : false,
41638     
41639     
41640     getSelection : function() 
41641     {
41642         this.assignDocWin();
41643         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41644     },
41645     
41646     getSelectedNode: function() 
41647     {
41648         // this may only work on Gecko!!!
41649         
41650         // should we cache this!!!!
41651         
41652         
41653         
41654          
41655         var range = this.createRange(this.getSelection()).cloneRange();
41656         
41657         if (Roo.isIE) {
41658             var parent = range.parentElement();
41659             while (true) {
41660                 var testRange = range.duplicate();
41661                 testRange.moveToElementText(parent);
41662                 if (testRange.inRange(range)) {
41663                     break;
41664                 }
41665                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41666                     break;
41667                 }
41668                 parent = parent.parentElement;
41669             }
41670             return parent;
41671         }
41672         
41673         // is ancestor a text element.
41674         var ac =  range.commonAncestorContainer;
41675         if (ac.nodeType == 3) {
41676             ac = ac.parentNode;
41677         }
41678         
41679         var ar = ac.childNodes;
41680          
41681         var nodes = [];
41682         var other_nodes = [];
41683         var has_other_nodes = false;
41684         for (var i=0;i<ar.length;i++) {
41685             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41686                 continue;
41687             }
41688             // fullly contained node.
41689             
41690             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41691                 nodes.push(ar[i]);
41692                 continue;
41693             }
41694             
41695             // probably selected..
41696             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41697                 other_nodes.push(ar[i]);
41698                 continue;
41699             }
41700             // outer..
41701             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41702                 continue;
41703             }
41704             
41705             
41706             has_other_nodes = true;
41707         }
41708         if (!nodes.length && other_nodes.length) {
41709             nodes= other_nodes;
41710         }
41711         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41712             return false;
41713         }
41714         
41715         return nodes[0];
41716     },
41717     createRange: function(sel)
41718     {
41719         // this has strange effects when using with 
41720         // top toolbar - not sure if it's a great idea.
41721         //this.editor.contentWindow.focus();
41722         if (typeof sel != "undefined") {
41723             try {
41724                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41725             } catch(e) {
41726                 return this.doc.createRange();
41727             }
41728         } else {
41729             return this.doc.createRange();
41730         }
41731     },
41732     getParentElement: function()
41733     {
41734         
41735         this.assignDocWin();
41736         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41737         
41738         var range = this.createRange(sel);
41739          
41740         try {
41741             var p = range.commonAncestorContainer;
41742             while (p.nodeType == 3) { // text node
41743                 p = p.parentNode;
41744             }
41745             return p;
41746         } catch (e) {
41747             return null;
41748         }
41749     
41750     },
41751     /***
41752      *
41753      * Range intersection.. the hard stuff...
41754      *  '-1' = before
41755      *  '0' = hits..
41756      *  '1' = after.
41757      *         [ -- selected range --- ]
41758      *   [fail]                        [fail]
41759      *
41760      *    basically..
41761      *      if end is before start or  hits it. fail.
41762      *      if start is after end or hits it fail.
41763      *
41764      *   if either hits (but other is outside. - then it's not 
41765      *   
41766      *    
41767      **/
41768     
41769     
41770     // @see http://www.thismuchiknow.co.uk/?p=64.
41771     rangeIntersectsNode : function(range, node)
41772     {
41773         var nodeRange = node.ownerDocument.createRange();
41774         try {
41775             nodeRange.selectNode(node);
41776         } catch (e) {
41777             nodeRange.selectNodeContents(node);
41778         }
41779     
41780         var rangeStartRange = range.cloneRange();
41781         rangeStartRange.collapse(true);
41782     
41783         var rangeEndRange = range.cloneRange();
41784         rangeEndRange.collapse(false);
41785     
41786         var nodeStartRange = nodeRange.cloneRange();
41787         nodeStartRange.collapse(true);
41788     
41789         var nodeEndRange = nodeRange.cloneRange();
41790         nodeEndRange.collapse(false);
41791     
41792         return rangeStartRange.compareBoundaryPoints(
41793                  Range.START_TO_START, nodeEndRange) == -1 &&
41794                rangeEndRange.compareBoundaryPoints(
41795                  Range.START_TO_START, nodeStartRange) == 1;
41796         
41797          
41798     },
41799     rangeCompareNode : function(range, node)
41800     {
41801         var nodeRange = node.ownerDocument.createRange();
41802         try {
41803             nodeRange.selectNode(node);
41804         } catch (e) {
41805             nodeRange.selectNodeContents(node);
41806         }
41807         
41808         
41809         range.collapse(true);
41810     
41811         nodeRange.collapse(true);
41812      
41813         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41814         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41815          
41816         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41817         
41818         var nodeIsBefore   =  ss == 1;
41819         var nodeIsAfter    = ee == -1;
41820         
41821         if (nodeIsBefore && nodeIsAfter)
41822             return 0; // outer
41823         if (!nodeIsBefore && nodeIsAfter)
41824             return 1; //right trailed.
41825         
41826         if (nodeIsBefore && !nodeIsAfter)
41827             return 2;  // left trailed.
41828         // fully contined.
41829         return 3;
41830     },
41831
41832     // private? - in a new class?
41833     cleanUpPaste :  function()
41834     {
41835         // cleans up the whole document..
41836         Roo.log('cleanuppaste');
41837         
41838         this.cleanUpChildren(this.doc.body);
41839         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41840         if (clean != this.doc.body.innerHTML) {
41841             this.doc.body.innerHTML = clean;
41842         }
41843         
41844     },
41845     
41846     cleanWordChars : function(input) {// change the chars to hex code
41847         var he = Roo.HtmlEditorCore;
41848         
41849         var output = input;
41850         Roo.each(he.swapCodes, function(sw) { 
41851             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41852             
41853             output = output.replace(swapper, sw[1]);
41854         });
41855         
41856         return output;
41857     },
41858     
41859     
41860     cleanUpChildren : function (n)
41861     {
41862         if (!n.childNodes.length) {
41863             return;
41864         }
41865         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41866            this.cleanUpChild(n.childNodes[i]);
41867         }
41868     },
41869     
41870     
41871         
41872     
41873     cleanUpChild : function (node)
41874     {
41875         var ed = this;
41876         //console.log(node);
41877         if (node.nodeName == "#text") {
41878             // clean up silly Windows -- stuff?
41879             return; 
41880         }
41881         if (node.nodeName == "#comment") {
41882             node.parentNode.removeChild(node);
41883             // clean up silly Windows -- stuff?
41884             return; 
41885         }
41886         
41887         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41888             // remove node.
41889             node.parentNode.removeChild(node);
41890             return;
41891             
41892         }
41893         
41894         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41895         
41896         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41897         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41898         
41899         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41900         //    remove_keep_children = true;
41901         //}
41902         
41903         if (remove_keep_children) {
41904             this.cleanUpChildren(node);
41905             // inserts everything just before this node...
41906             while (node.childNodes.length) {
41907                 var cn = node.childNodes[0];
41908                 node.removeChild(cn);
41909                 node.parentNode.insertBefore(cn, node);
41910             }
41911             node.parentNode.removeChild(node);
41912             return;
41913         }
41914         
41915         if (!node.attributes || !node.attributes.length) {
41916             this.cleanUpChildren(node);
41917             return;
41918         }
41919         
41920         function cleanAttr(n,v)
41921         {
41922             
41923             if (v.match(/^\./) || v.match(/^\//)) {
41924                 return;
41925             }
41926             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41927                 return;
41928             }
41929             if (v.match(/^#/)) {
41930                 return;
41931             }
41932 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41933             node.removeAttribute(n);
41934             
41935         }
41936         
41937         function cleanStyle(n,v)
41938         {
41939             if (v.match(/expression/)) { //XSS?? should we even bother..
41940                 node.removeAttribute(n);
41941                 return;
41942             }
41943             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
41944             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
41945             
41946             
41947             var parts = v.split(/;/);
41948             var clean = [];
41949             
41950             Roo.each(parts, function(p) {
41951                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41952                 if (!p.length) {
41953                     return true;
41954                 }
41955                 var l = p.split(':').shift().replace(/\s+/g,'');
41956                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41957                 
41958                 if ( cblack.indexOf(l) > -1) {
41959 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41960                     //node.removeAttribute(n);
41961                     return true;
41962                 }
41963                 //Roo.log()
41964                 // only allow 'c whitelisted system attributes'
41965                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41966 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41967                     //node.removeAttribute(n);
41968                     return true;
41969                 }
41970                 
41971                 
41972                  
41973                 
41974                 clean.push(p);
41975                 return true;
41976             });
41977             if (clean.length) { 
41978                 node.setAttribute(n, clean.join(';'));
41979             } else {
41980                 node.removeAttribute(n);
41981             }
41982             
41983         }
41984         
41985         
41986         for (var i = node.attributes.length-1; i > -1 ; i--) {
41987             var a = node.attributes[i];
41988             //console.log(a);
41989             
41990             if (a.name.toLowerCase().substr(0,2)=='on')  {
41991                 node.removeAttribute(a.name);
41992                 continue;
41993             }
41994             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
41995                 node.removeAttribute(a.name);
41996                 continue;
41997             }
41998             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
41999                 cleanAttr(a.name,a.value); // fixme..
42000                 continue;
42001             }
42002             if (a.name == 'style') {
42003                 cleanStyle(a.name,a.value);
42004                 continue;
42005             }
42006             /// clean up MS crap..
42007             // tecnically this should be a list of valid class'es..
42008             
42009             
42010             if (a.name == 'class') {
42011                 if (a.value.match(/^Mso/)) {
42012                     node.className = '';
42013                 }
42014                 
42015                 if (a.value.match(/body/)) {
42016                     node.className = '';
42017                 }
42018                 continue;
42019             }
42020             
42021             // style cleanup!?
42022             // class cleanup?
42023             
42024         }
42025         
42026         
42027         this.cleanUpChildren(node);
42028         
42029         
42030     },
42031     /**
42032      * Clean up MS wordisms...
42033      */
42034     cleanWord : function(node)
42035     {
42036         var _t = this;
42037         var cleanWordChildren = function()
42038         {
42039             if (!node.childNodes.length) {
42040                 return;
42041             }
42042             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42043                _t.cleanWord(node.childNodes[i]);
42044             }
42045         }
42046         
42047         
42048         if (!node) {
42049             this.cleanWord(this.doc.body);
42050             return;
42051         }
42052         if (node.nodeName == "#text") {
42053             // clean up silly Windows -- stuff?
42054             return; 
42055         }
42056         if (node.nodeName == "#comment") {
42057             node.parentNode.removeChild(node);
42058             // clean up silly Windows -- stuff?
42059             return; 
42060         }
42061         
42062         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42063             node.parentNode.removeChild(node);
42064             return;
42065         }
42066         
42067         // remove - but keep children..
42068         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42069             while (node.childNodes.length) {
42070                 var cn = node.childNodes[0];
42071                 node.removeChild(cn);
42072                 node.parentNode.insertBefore(cn, node);
42073             }
42074             node.parentNode.removeChild(node);
42075             cleanWordChildren();
42076             return;
42077         }
42078         // clean styles
42079         if (node.className.length) {
42080             
42081             var cn = node.className.split(/\W+/);
42082             var cna = [];
42083             Roo.each(cn, function(cls) {
42084                 if (cls.match(/Mso[a-zA-Z]+/)) {
42085                     return;
42086                 }
42087                 cna.push(cls);
42088             });
42089             node.className = cna.length ? cna.join(' ') : '';
42090             if (!cna.length) {
42091                 node.removeAttribute("class");
42092             }
42093         }
42094         
42095         if (node.hasAttribute("lang")) {
42096             node.removeAttribute("lang");
42097         }
42098         
42099         if (node.hasAttribute("style")) {
42100             
42101             var styles = node.getAttribute("style").split(";");
42102             var nstyle = [];
42103             Roo.each(styles, function(s) {
42104                 if (!s.match(/:/)) {
42105                     return;
42106                 }
42107                 var kv = s.split(":");
42108                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42109                     return;
42110                 }
42111                 // what ever is left... we allow.
42112                 nstyle.push(s);
42113             });
42114             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42115             if (!nstyle.length) {
42116                 node.removeAttribute('style');
42117             }
42118         }
42119         
42120         cleanWordChildren();
42121         
42122         
42123     },
42124     domToHTML : function(currentElement, depth, nopadtext) {
42125         
42126             depth = depth || 0;
42127             nopadtext = nopadtext || false;
42128         
42129             if (!currentElement) {
42130                 return this.domToHTML(this.doc.body);
42131             }
42132             
42133             //Roo.log(currentElement);
42134             var j;
42135             var allText = false;
42136             var nodeName = currentElement.nodeName;
42137             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42138             
42139             if  (nodeName == '#text') {
42140                 return currentElement.nodeValue;
42141             }
42142             
42143             
42144             var ret = '';
42145             if (nodeName != 'BODY') {
42146                  
42147                 var i = 0;
42148                 // Prints the node tagName, such as <A>, <IMG>, etc
42149                 if (tagName) {
42150                     var attr = [];
42151                     for(i = 0; i < currentElement.attributes.length;i++) {
42152                         // quoting?
42153                         var aname = currentElement.attributes.item(i).name;
42154                         if (!currentElement.attributes.item(i).value.length) {
42155                             continue;
42156                         }
42157                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42158                     }
42159                     
42160                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42161                 } 
42162                 else {
42163                     
42164                     // eack
42165                 }
42166             } else {
42167                 tagName = false;
42168             }
42169             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42170                 return ret;
42171             }
42172             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42173                 nopadtext = true;
42174             }
42175             
42176             
42177             // Traverse the tree
42178             i = 0;
42179             var currentElementChild = currentElement.childNodes.item(i);
42180             var allText = true;
42181             var innerHTML  = '';
42182             lastnode = '';
42183             while (currentElementChild) {
42184                 // Formatting code (indent the tree so it looks nice on the screen)
42185                 var nopad = nopadtext;
42186                 if (lastnode == 'SPAN') {
42187                     nopad  = true;
42188                 }
42189                 // text
42190                 if  (currentElementChild.nodeName == '#text') {
42191                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42192                     if (!nopad && toadd.length > 80) {
42193                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42194                     }
42195                     innerHTML  += toadd;
42196                     
42197                     i++;
42198                     currentElementChild = currentElement.childNodes.item(i);
42199                     lastNode = '';
42200                     continue;
42201                 }
42202                 allText = false;
42203                 
42204                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42205                     
42206                 // Recursively traverse the tree structure of the child node
42207                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42208                 lastnode = currentElementChild.nodeName;
42209                 i++;
42210                 currentElementChild=currentElement.childNodes.item(i);
42211             }
42212             
42213             ret += innerHTML;
42214             
42215             if (!allText) {
42216                     // The remaining code is mostly for formatting the tree
42217                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42218             }
42219             
42220             
42221             if (tagName) {
42222                 ret+= "</"+tagName+">";
42223             }
42224             return ret;
42225             
42226         }
42227     
42228     // hide stuff that is not compatible
42229     /**
42230      * @event blur
42231      * @hide
42232      */
42233     /**
42234      * @event change
42235      * @hide
42236      */
42237     /**
42238      * @event focus
42239      * @hide
42240      */
42241     /**
42242      * @event specialkey
42243      * @hide
42244      */
42245     /**
42246      * @cfg {String} fieldClass @hide
42247      */
42248     /**
42249      * @cfg {String} focusClass @hide
42250      */
42251     /**
42252      * @cfg {String} autoCreate @hide
42253      */
42254     /**
42255      * @cfg {String} inputType @hide
42256      */
42257     /**
42258      * @cfg {String} invalidClass @hide
42259      */
42260     /**
42261      * @cfg {String} invalidText @hide
42262      */
42263     /**
42264      * @cfg {String} msgFx @hide
42265      */
42266     /**
42267      * @cfg {String} validateOnBlur @hide
42268      */
42269 });
42270
42271 Roo.HtmlEditorCore.white = [
42272         'area', 'br', 'img', 'input', 'hr', 'wbr',
42273         
42274        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42275        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42276        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42277        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42278        'table',   'ul',         'xmp', 
42279        
42280        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42281       'thead',   'tr', 
42282      
42283       'dir', 'menu', 'ol', 'ul', 'dl',
42284        
42285       'embed',  'object'
42286 ];
42287
42288
42289 Roo.HtmlEditorCore.black = [
42290     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42291         'applet', // 
42292         'base',   'basefont', 'bgsound', 'blink',  'body', 
42293         'frame',  'frameset', 'head',    'html',   'ilayer', 
42294         'iframe', 'layer',  'link',     'meta',    'object',   
42295         'script', 'style' ,'title',  'xml' // clean later..
42296 ];
42297 Roo.HtmlEditorCore.clean = [
42298     'script', 'style', 'title', 'xml'
42299 ];
42300 Roo.HtmlEditorCore.remove = [
42301     'font'
42302 ];
42303 // attributes..
42304
42305 Roo.HtmlEditorCore.ablack = [
42306     'on'
42307 ];
42308     
42309 Roo.HtmlEditorCore.aclean = [ 
42310     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42311 ];
42312
42313 // protocols..
42314 Roo.HtmlEditorCore.pwhite= [
42315         'http',  'https',  'mailto'
42316 ];
42317
42318 // white listed style attributes.
42319 Roo.HtmlEditorCore.cwhite= [
42320       //  'text-align', /// default is to allow most things..
42321       
42322          
42323 //        'font-size'//??
42324 ];
42325
42326 // black listed style attributes.
42327 Roo.HtmlEditorCore.cblack= [
42328       //  'font-size' -- this can be set by the project 
42329 ];
42330
42331
42332 Roo.HtmlEditorCore.swapCodes   =[ 
42333     [    8211, "--" ], 
42334     [    8212, "--" ], 
42335     [    8216,  "'" ],  
42336     [    8217, "'" ],  
42337     [    8220, '"' ],  
42338     [    8221, '"' ],  
42339     [    8226, "*" ],  
42340     [    8230, "..." ]
42341 ]; 
42342
42343     //<script type="text/javascript">
42344
42345 /*
42346  * Ext JS Library 1.1.1
42347  * Copyright(c) 2006-2007, Ext JS, LLC.
42348  * Licence LGPL
42349  * 
42350  */
42351  
42352  
42353 Roo.form.HtmlEditor = function(config){
42354     
42355     
42356     
42357     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42358     
42359     if (!this.toolbars) {
42360         this.toolbars = [];
42361     }
42362     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42363     
42364     
42365 };
42366
42367 /**
42368  * @class Roo.form.HtmlEditor
42369  * @extends Roo.form.Field
42370  * Provides a lightweight HTML Editor component.
42371  *
42372  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42373  * 
42374  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42375  * supported by this editor.</b><br/><br/>
42376  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42377  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42378  */
42379 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42380     /**
42381      * @cfg {Boolean} clearUp
42382      */
42383     clearUp : true,
42384       /**
42385      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42386      */
42387     toolbars : false,
42388    
42389      /**
42390      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42391      *                        Roo.resizable.
42392      */
42393     resizable : false,
42394      /**
42395      * @cfg {Number} height (in pixels)
42396      */   
42397     height: 300,
42398    /**
42399      * @cfg {Number} width (in pixels)
42400      */   
42401     width: 500,
42402     
42403     /**
42404      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42405      * 
42406      */
42407     stylesheets: false,
42408     
42409     // id of frame..
42410     frameId: false,
42411     
42412     // private properties
42413     validationEvent : false,
42414     deferHeight: true,
42415     initialized : false,
42416     activated : false,
42417     
42418     onFocus : Roo.emptyFn,
42419     iframePad:3,
42420     hideMode:'offsets',
42421     
42422     defaultAutoCreate : { // modified by initCompnoent..
42423         tag: "textarea",
42424         style:"width:500px;height:300px;",
42425         autocomplete: "off"
42426     },
42427
42428     // private
42429     initComponent : function(){
42430         this.addEvents({
42431             /**
42432              * @event initialize
42433              * Fires when the editor is fully initialized (including the iframe)
42434              * @param {HtmlEditor} this
42435              */
42436             initialize: true,
42437             /**
42438              * @event activate
42439              * Fires when the editor is first receives the focus. Any insertion must wait
42440              * until after this event.
42441              * @param {HtmlEditor} this
42442              */
42443             activate: true,
42444              /**
42445              * @event beforesync
42446              * Fires before the textarea is updated with content from the editor iframe. Return false
42447              * to cancel the sync.
42448              * @param {HtmlEditor} this
42449              * @param {String} html
42450              */
42451             beforesync: true,
42452              /**
42453              * @event beforepush
42454              * Fires before the iframe editor is updated with content from the textarea. Return false
42455              * to cancel the push.
42456              * @param {HtmlEditor} this
42457              * @param {String} html
42458              */
42459             beforepush: true,
42460              /**
42461              * @event sync
42462              * Fires when the textarea is updated with content from the editor iframe.
42463              * @param {HtmlEditor} this
42464              * @param {String} html
42465              */
42466             sync: true,
42467              /**
42468              * @event push
42469              * Fires when the iframe editor is updated with content from the textarea.
42470              * @param {HtmlEditor} this
42471              * @param {String} html
42472              */
42473             push: true,
42474              /**
42475              * @event editmodechange
42476              * Fires when the editor switches edit modes
42477              * @param {HtmlEditor} this
42478              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42479              */
42480             editmodechange: true,
42481             /**
42482              * @event editorevent
42483              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42484              * @param {HtmlEditor} this
42485              */
42486             editorevent: true,
42487             /**
42488              * @event firstfocus
42489              * Fires when on first focus - needed by toolbars..
42490              * @param {HtmlEditor} this
42491              */
42492             firstfocus: true,
42493             /**
42494              * @event autosave
42495              * Auto save the htmlEditor value as a file into Events
42496              * @param {HtmlEditor} this
42497              */
42498             autosave: true,
42499             /**
42500              * @event savedpreview
42501              * preview the saved version of htmlEditor
42502              * @param {HtmlEditor} this
42503              */
42504             savedpreview: true
42505         });
42506         this.defaultAutoCreate =  {
42507             tag: "textarea",
42508             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42509             autocomplete: "off"
42510         };
42511     },
42512
42513     /**
42514      * Protected method that will not generally be called directly. It
42515      * is called when the editor creates its toolbar. Override this method if you need to
42516      * add custom toolbar buttons.
42517      * @param {HtmlEditor} editor
42518      */
42519     createToolbar : function(editor){
42520         Roo.log("create toolbars");
42521         if (!editor.toolbars || !editor.toolbars.length) {
42522             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42523         }
42524         
42525         for (var i =0 ; i < editor.toolbars.length;i++) {
42526             editor.toolbars[i] = Roo.factory(
42527                     typeof(editor.toolbars[i]) == 'string' ?
42528                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42529                 Roo.form.HtmlEditor);
42530             editor.toolbars[i].init(editor);
42531         }
42532          
42533         
42534     },
42535
42536      
42537     // private
42538     onRender : function(ct, position)
42539     {
42540         var _t = this;
42541         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42542         
42543         this.wrap = this.el.wrap({
42544             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42545         });
42546         
42547         this.editorcore.onRender(ct, position);
42548          
42549         if (this.resizable) {
42550             this.resizeEl = new Roo.Resizable(this.wrap, {
42551                 pinned : true,
42552                 wrap: true,
42553                 dynamic : true,
42554                 minHeight : this.height,
42555                 height: this.height,
42556                 handles : this.resizable,
42557                 width: this.width,
42558                 listeners : {
42559                     resize : function(r, w, h) {
42560                         _t.onResize(w,h); // -something
42561                     }
42562                 }
42563             });
42564             
42565         }
42566         this.createToolbar(this);
42567        
42568         
42569         if(!this.width){
42570             this.setSize(this.wrap.getSize());
42571         }
42572         if (this.resizeEl) {
42573             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42574             // should trigger onReize..
42575         }
42576         
42577 //        if(this.autosave && this.w){
42578 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42579 //        }
42580     },
42581
42582     // private
42583     onResize : function(w, h)
42584     {
42585         //Roo.log('resize: ' +w + ',' + h );
42586         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42587         var ew = false;
42588         var eh = false;
42589         
42590         if(this.el ){
42591             if(typeof w == 'number'){
42592                 var aw = w - this.wrap.getFrameWidth('lr');
42593                 this.el.setWidth(this.adjustWidth('textarea', aw));
42594                 ew = aw;
42595             }
42596             if(typeof h == 'number'){
42597                 var tbh = 0;
42598                 for (var i =0; i < this.toolbars.length;i++) {
42599                     // fixme - ask toolbars for heights?
42600                     tbh += this.toolbars[i].tb.el.getHeight();
42601                     if (this.toolbars[i].footer) {
42602                         tbh += this.toolbars[i].footer.el.getHeight();
42603                     }
42604                 }
42605                 
42606                 
42607                 
42608                 
42609                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42610                 ah -= 5; // knock a few pixes off for look..
42611                 this.el.setHeight(this.adjustWidth('textarea', ah));
42612                 var eh = ah;
42613             }
42614         }
42615         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42616         this.editorcore.onResize(ew,eh);
42617         
42618     },
42619
42620     /**
42621      * Toggles the editor between standard and source edit mode.
42622      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42623      */
42624     toggleSourceEdit : function(sourceEditMode)
42625     {
42626         this.editorcore.toggleSourceEdit(sourceEditMode);
42627         
42628         if(this.editorcore.sourceEditMode){
42629             Roo.log('editor - showing textarea');
42630             
42631 //            Roo.log('in');
42632 //            Roo.log(this.syncValue());
42633             this.editorcore.syncValue();
42634             this.el.removeClass('x-hidden');
42635             this.el.dom.removeAttribute('tabIndex');
42636             this.el.focus();
42637         }else{
42638             Roo.log('editor - hiding textarea');
42639 //            Roo.log('out')
42640 //            Roo.log(this.pushValue()); 
42641             this.editorcore.pushValue();
42642             
42643             this.el.addClass('x-hidden');
42644             this.el.dom.setAttribute('tabIndex', -1);
42645             //this.deferFocus();
42646         }
42647          
42648         this.setSize(this.wrap.getSize());
42649         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42650     },
42651  
42652     // private (for BoxComponent)
42653     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42654
42655     // private (for BoxComponent)
42656     getResizeEl : function(){
42657         return this.wrap;
42658     },
42659
42660     // private (for BoxComponent)
42661     getPositionEl : function(){
42662         return this.wrap;
42663     },
42664
42665     // private
42666     initEvents : function(){
42667         this.originalValue = this.getValue();
42668     },
42669
42670     /**
42671      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42672      * @method
42673      */
42674     markInvalid : Roo.emptyFn,
42675     /**
42676      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42677      * @method
42678      */
42679     clearInvalid : Roo.emptyFn,
42680
42681     setValue : function(v){
42682         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42683         this.editorcore.pushValue();
42684     },
42685
42686      
42687     // private
42688     deferFocus : function(){
42689         this.focus.defer(10, this);
42690     },
42691
42692     // doc'ed in Field
42693     focus : function(){
42694         this.editorcore.focus();
42695         
42696     },
42697       
42698
42699     // private
42700     onDestroy : function(){
42701         
42702         
42703         
42704         if(this.rendered){
42705             
42706             for (var i =0; i < this.toolbars.length;i++) {
42707                 // fixme - ask toolbars for heights?
42708                 this.toolbars[i].onDestroy();
42709             }
42710             
42711             this.wrap.dom.innerHTML = '';
42712             this.wrap.remove();
42713         }
42714     },
42715
42716     // private
42717     onFirstFocus : function(){
42718         //Roo.log("onFirstFocus");
42719         this.editorcore.onFirstFocus();
42720          for (var i =0; i < this.toolbars.length;i++) {
42721             this.toolbars[i].onFirstFocus();
42722         }
42723         
42724     },
42725     
42726     // private
42727     syncValue : function()
42728     {
42729         this.editorcore.syncValue();
42730     },
42731     
42732     pushValue : function()
42733     {
42734         this.editorcore.pushValue();
42735     }
42736      
42737     
42738     // hide stuff that is not compatible
42739     /**
42740      * @event blur
42741      * @hide
42742      */
42743     /**
42744      * @event change
42745      * @hide
42746      */
42747     /**
42748      * @event focus
42749      * @hide
42750      */
42751     /**
42752      * @event specialkey
42753      * @hide
42754      */
42755     /**
42756      * @cfg {String} fieldClass @hide
42757      */
42758     /**
42759      * @cfg {String} focusClass @hide
42760      */
42761     /**
42762      * @cfg {String} autoCreate @hide
42763      */
42764     /**
42765      * @cfg {String} inputType @hide
42766      */
42767     /**
42768      * @cfg {String} invalidClass @hide
42769      */
42770     /**
42771      * @cfg {String} invalidText @hide
42772      */
42773     /**
42774      * @cfg {String} msgFx @hide
42775      */
42776     /**
42777      * @cfg {String} validateOnBlur @hide
42778      */
42779 });
42780  
42781     // <script type="text/javascript">
42782 /*
42783  * Based on
42784  * Ext JS Library 1.1.1
42785  * Copyright(c) 2006-2007, Ext JS, LLC.
42786  *  
42787  
42788  */
42789
42790 /**
42791  * @class Roo.form.HtmlEditorToolbar1
42792  * Basic Toolbar
42793  * 
42794  * Usage:
42795  *
42796  new Roo.form.HtmlEditor({
42797     ....
42798     toolbars : [
42799         new Roo.form.HtmlEditorToolbar1({
42800             disable : { fonts: 1 , format: 1, ..., ... , ...],
42801             btns : [ .... ]
42802         })
42803     }
42804      
42805  * 
42806  * @cfg {Object} disable List of elements to disable..
42807  * @cfg {Array} btns List of additional buttons.
42808  * 
42809  * 
42810  * NEEDS Extra CSS? 
42811  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42812  */
42813  
42814 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42815 {
42816     
42817     Roo.apply(this, config);
42818     
42819     // default disabled, based on 'good practice'..
42820     this.disable = this.disable || {};
42821     Roo.applyIf(this.disable, {
42822         fontSize : true,
42823         colors : true,
42824         specialElements : true
42825     });
42826     
42827     
42828     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42829     // dont call parent... till later.
42830 }
42831
42832 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42833     
42834     tb: false,
42835     
42836     rendered: false,
42837     
42838     editor : false,
42839     editorcore : false,
42840     /**
42841      * @cfg {Object} disable  List of toolbar elements to disable
42842          
42843      */
42844     disable : false,
42845     
42846     
42847      /**
42848      * @cfg {String} createLinkText The default text for the create link prompt
42849      */
42850     createLinkText : 'Please enter the URL for the link:',
42851     /**
42852      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42853      */
42854     defaultLinkValue : 'http:/'+'/',
42855    
42856     
42857       /**
42858      * @cfg {Array} fontFamilies An array of available font families
42859      */
42860     fontFamilies : [
42861         'Arial',
42862         'Courier New',
42863         'Tahoma',
42864         'Times New Roman',
42865         'Verdana'
42866     ],
42867     
42868     specialChars : [
42869            "&#169;",
42870           "&#174;",     
42871           "&#8482;",    
42872           "&#163;" ,    
42873          // "&#8212;",    
42874           "&#8230;",    
42875           "&#247;" ,    
42876         //  "&#225;" ,     ?? a acute?
42877            "&#8364;"    , //Euro
42878        //   "&#8220;"    ,
42879         //  "&#8221;"    ,
42880         //  "&#8226;"    ,
42881           "&#176;"  //   , // degrees
42882
42883          // "&#233;"     , // e ecute
42884          // "&#250;"     , // u ecute?
42885     ],
42886     
42887     specialElements : [
42888         {
42889             text: "Insert Table",
42890             xtype: 'MenuItem',
42891             xns : Roo.Menu,
42892             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42893                 
42894         },
42895         {    
42896             text: "Insert Image",
42897             xtype: 'MenuItem',
42898             xns : Roo.Menu,
42899             ihtml : '<img src="about:blank"/>'
42900             
42901         }
42902         
42903          
42904     ],
42905     
42906     
42907     inputElements : [ 
42908             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42909             "input:submit", "input:button", "select", "textarea", "label" ],
42910     formats : [
42911         ["p"] ,  
42912         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42913         ["pre"],[ "code"], 
42914         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42915         ['div'],['span']
42916     ],
42917     
42918     cleanStyles : [
42919         "font-size"
42920     ],
42921      /**
42922      * @cfg {String} defaultFont default font to use.
42923      */
42924     defaultFont: 'tahoma',
42925    
42926     fontSelect : false,
42927     
42928     
42929     formatCombo : false,
42930     
42931     init : function(editor)
42932     {
42933         this.editor = editor;
42934         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42935         var editorcore = this.editorcore;
42936         
42937         var _t = this;
42938         
42939         var fid = editorcore.frameId;
42940         var etb = this;
42941         function btn(id, toggle, handler){
42942             var xid = fid + '-'+ id ;
42943             return {
42944                 id : xid,
42945                 cmd : id,
42946                 cls : 'x-btn-icon x-edit-'+id,
42947                 enableToggle:toggle !== false,
42948                 scope: _t, // was editor...
42949                 handler:handler||_t.relayBtnCmd,
42950                 clickEvent:'mousedown',
42951                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42952                 tabIndex:-1
42953             };
42954         }
42955         
42956         
42957         
42958         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42959         this.tb = tb;
42960          // stop form submits
42961         tb.el.on('click', function(e){
42962             e.preventDefault(); // what does this do?
42963         });
42964
42965         if(!this.disable.font) { // && !Roo.isSafari){
42966             /* why no safari for fonts 
42967             editor.fontSelect = tb.el.createChild({
42968                 tag:'select',
42969                 tabIndex: -1,
42970                 cls:'x-font-select',
42971                 html: this.createFontOptions()
42972             });
42973             
42974             editor.fontSelect.on('change', function(){
42975                 var font = editor.fontSelect.dom.value;
42976                 editor.relayCmd('fontname', font);
42977                 editor.deferFocus();
42978             }, editor);
42979             
42980             tb.add(
42981                 editor.fontSelect.dom,
42982                 '-'
42983             );
42984             */
42985             
42986         };
42987         if(!this.disable.formats){
42988             this.formatCombo = new Roo.form.ComboBox({
42989                 store: new Roo.data.SimpleStore({
42990                     id : 'tag',
42991                     fields: ['tag'],
42992                     data : this.formats // from states.js
42993                 }),
42994                 blockFocus : true,
42995                 name : '',
42996                 //autoCreate : {tag: "div",  size: "20"},
42997                 displayField:'tag',
42998                 typeAhead: false,
42999                 mode: 'local',
43000                 editable : false,
43001                 triggerAction: 'all',
43002                 emptyText:'Add tag',
43003                 selectOnFocus:true,
43004                 width:135,
43005                 listeners : {
43006                     'select': function(c, r, i) {
43007                         editorcore.insertTag(r.get('tag'));
43008                         editor.focus();
43009                     }
43010                 }
43011
43012             });
43013             tb.addField(this.formatCombo);
43014             
43015         }
43016         
43017         if(!this.disable.format){
43018             tb.add(
43019                 btn('bold'),
43020                 btn('italic'),
43021                 btn('underline')
43022             );
43023         };
43024         if(!this.disable.fontSize){
43025             tb.add(
43026                 '-',
43027                 
43028                 
43029                 btn('increasefontsize', false, editorcore.adjustFont),
43030                 btn('decreasefontsize', false, editorcore.adjustFont)
43031             );
43032         };
43033         
43034         
43035         if(!this.disable.colors){
43036             tb.add(
43037                 '-', {
43038                     id:editorcore.frameId +'-forecolor',
43039                     cls:'x-btn-icon x-edit-forecolor',
43040                     clickEvent:'mousedown',
43041                     tooltip: this.buttonTips['forecolor'] || undefined,
43042                     tabIndex:-1,
43043                     menu : new Roo.menu.ColorMenu({
43044                         allowReselect: true,
43045                         focus: Roo.emptyFn,
43046                         value:'000000',
43047                         plain:true,
43048                         selectHandler: function(cp, color){
43049                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43050                             editor.deferFocus();
43051                         },
43052                         scope: editorcore,
43053                         clickEvent:'mousedown'
43054                     })
43055                 }, {
43056                     id:editorcore.frameId +'backcolor',
43057                     cls:'x-btn-icon x-edit-backcolor',
43058                     clickEvent:'mousedown',
43059                     tooltip: this.buttonTips['backcolor'] || undefined,
43060                     tabIndex:-1,
43061                     menu : new Roo.menu.ColorMenu({
43062                         focus: Roo.emptyFn,
43063                         value:'FFFFFF',
43064                         plain:true,
43065                         allowReselect: true,
43066                         selectHandler: function(cp, color){
43067                             if(Roo.isGecko){
43068                                 editorcore.execCmd('useCSS', false);
43069                                 editorcore.execCmd('hilitecolor', color);
43070                                 editorcore.execCmd('useCSS', true);
43071                                 editor.deferFocus();
43072                             }else{
43073                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43074                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43075                                 editor.deferFocus();
43076                             }
43077                         },
43078                         scope:editorcore,
43079                         clickEvent:'mousedown'
43080                     })
43081                 }
43082             );
43083         };
43084         // now add all the items...
43085         
43086
43087         if(!this.disable.alignments){
43088             tb.add(
43089                 '-',
43090                 btn('justifyleft'),
43091                 btn('justifycenter'),
43092                 btn('justifyright')
43093             );
43094         };
43095
43096         //if(!Roo.isSafari){
43097             if(!this.disable.links){
43098                 tb.add(
43099                     '-',
43100                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43101                 );
43102             };
43103
43104             if(!this.disable.lists){
43105                 tb.add(
43106                     '-',
43107                     btn('insertorderedlist'),
43108                     btn('insertunorderedlist')
43109                 );
43110             }
43111             if(!this.disable.sourceEdit){
43112                 tb.add(
43113                     '-',
43114                     btn('sourceedit', true, function(btn){
43115                         Roo.log(this);
43116                         this.toggleSourceEdit(btn.pressed);
43117                     })
43118                 );
43119             }
43120         //}
43121         
43122         var smenu = { };
43123         // special menu.. - needs to be tidied up..
43124         if (!this.disable.special) {
43125             smenu = {
43126                 text: "&#169;",
43127                 cls: 'x-edit-none',
43128                 
43129                 menu : {
43130                     items : []
43131                 }
43132             };
43133             for (var i =0; i < this.specialChars.length; i++) {
43134                 smenu.menu.items.push({
43135                     
43136                     html: this.specialChars[i],
43137                     handler: function(a,b) {
43138                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43139                         //editor.insertAtCursor(a.html);
43140                         
43141                     },
43142                     tabIndex:-1
43143                 });
43144             }
43145             
43146             
43147             tb.add(smenu);
43148             
43149             
43150         }
43151         
43152         var cmenu = { };
43153         if (!this.disable.cleanStyles) {
43154             cmenu = {
43155                 cls: 'x-btn-icon x-btn-clear',
43156                 
43157                 menu : {
43158                     items : []
43159                 }
43160             };
43161             for (var i =0; i < this.cleanStyles.length; i++) {
43162                 cmenu.menu.items.push({
43163                     actiontype : this.cleanStyles[i],
43164                     html: 'Remove ' + this.cleanStyles[i],
43165                     handler: function(a,b) {
43166                         Roo.log(a);
43167                         Roo.log(b);
43168                         var c = Roo.get(editorcore.doc.body);
43169                         c.select('[style]').each(function(s) {
43170                             s.dom.style.removeProperty(a.actiontype);
43171                         });
43172                         editorcore.syncValue();
43173                     },
43174                     tabIndex:-1
43175                 });
43176             }
43177             cmenu.menu.items.push({
43178                 actiontype : 'word',
43179                 html: 'Remove MS Word Formating',
43180                 handler: function(a,b) {
43181                     editorcore.cleanWord();
43182                     editorcore.syncValue();
43183                 },
43184                 tabIndex:-1
43185             });
43186             
43187             cmenu.menu.items.push({
43188                 actiontype : 'all',
43189                 html: 'Remove All Styles',
43190                 handler: function(a,b) {
43191                     
43192                     var c = Roo.get(editorcore.doc.body);
43193                     c.select('[style]').each(function(s) {
43194                         s.dom.removeAttribute('style');
43195                     });
43196                     editorcore.syncValue();
43197                 },
43198                 tabIndex:-1
43199             });
43200              cmenu.menu.items.push({
43201                 actiontype : 'word',
43202                 html: 'Tidy HTML Source',
43203                 handler: function(a,b) {
43204                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43205                     editorcore.syncValue();
43206                 },
43207                 tabIndex:-1
43208             });
43209             
43210             
43211             tb.add(cmenu);
43212         }
43213          
43214         if (!this.disable.specialElements) {
43215             var semenu = {
43216                 text: "Other;",
43217                 cls: 'x-edit-none',
43218                 menu : {
43219                     items : []
43220                 }
43221             };
43222             for (var i =0; i < this.specialElements.length; i++) {
43223                 semenu.menu.items.push(
43224                     Roo.apply({ 
43225                         handler: function(a,b) {
43226                             editor.insertAtCursor(this.ihtml);
43227                         }
43228                     }, this.specialElements[i])
43229                 );
43230                     
43231             }
43232             
43233             tb.add(semenu);
43234             
43235             
43236         }
43237          
43238         
43239         if (this.btns) {
43240             for(var i =0; i< this.btns.length;i++) {
43241                 var b = Roo.factory(this.btns[i],Roo.form);
43242                 b.cls =  'x-edit-none';
43243                 b.scope = editorcore;
43244                 tb.add(b);
43245             }
43246         
43247         }
43248         
43249         
43250         
43251         // disable everything...
43252         
43253         this.tb.items.each(function(item){
43254            if(item.id != editorcore.frameId+ '-sourceedit'){
43255                 item.disable();
43256             }
43257         });
43258         this.rendered = true;
43259         
43260         // the all the btns;
43261         editor.on('editorevent', this.updateToolbar, this);
43262         // other toolbars need to implement this..
43263         //editor.on('editmodechange', this.updateToolbar, this);
43264     },
43265     
43266     
43267     relayBtnCmd : function(btn) {
43268         this.editorcore.relayCmd(btn.cmd);
43269     },
43270     // private used internally
43271     createLink : function(){
43272         Roo.log("create link?");
43273         var url = prompt(this.createLinkText, this.defaultLinkValue);
43274         if(url && url != 'http:/'+'/'){
43275             this.editorcore.relayCmd('createlink', url);
43276         }
43277     },
43278
43279     
43280     /**
43281      * Protected method that will not generally be called directly. It triggers
43282      * a toolbar update by reading the markup state of the current selection in the editor.
43283      */
43284     updateToolbar: function(){
43285
43286         if(!this.editorcore.activated){
43287             this.editor.onFirstFocus();
43288             return;
43289         }
43290
43291         var btns = this.tb.items.map, 
43292             doc = this.editorcore.doc,
43293             frameId = this.editorcore.frameId;
43294
43295         if(!this.disable.font && !Roo.isSafari){
43296             /*
43297             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43298             if(name != this.fontSelect.dom.value){
43299                 this.fontSelect.dom.value = name;
43300             }
43301             */
43302         }
43303         if(!this.disable.format){
43304             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43305             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43306             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43307         }
43308         if(!this.disable.alignments){
43309             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43310             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43311             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43312         }
43313         if(!Roo.isSafari && !this.disable.lists){
43314             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43315             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43316         }
43317         
43318         var ans = this.editorcore.getAllAncestors();
43319         if (this.formatCombo) {
43320             
43321             
43322             var store = this.formatCombo.store;
43323             this.formatCombo.setValue("");
43324             for (var i =0; i < ans.length;i++) {
43325                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43326                     // select it..
43327                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43328                     break;
43329                 }
43330             }
43331         }
43332         
43333         
43334         
43335         // hides menus... - so this cant be on a menu...
43336         Roo.menu.MenuMgr.hideAll();
43337
43338         //this.editorsyncValue();
43339     },
43340    
43341     
43342     createFontOptions : function(){
43343         var buf = [], fs = this.fontFamilies, ff, lc;
43344         
43345         
43346         
43347         for(var i = 0, len = fs.length; i< len; i++){
43348             ff = fs[i];
43349             lc = ff.toLowerCase();
43350             buf.push(
43351                 '<option value="',lc,'" style="font-family:',ff,';"',
43352                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43353                     ff,
43354                 '</option>'
43355             );
43356         }
43357         return buf.join('');
43358     },
43359     
43360     toggleSourceEdit : function(sourceEditMode){
43361         
43362         Roo.log("toolbar toogle");
43363         if(sourceEditMode === undefined){
43364             sourceEditMode = !this.sourceEditMode;
43365         }
43366         this.sourceEditMode = sourceEditMode === true;
43367         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43368         // just toggle the button?
43369         if(btn.pressed !== this.sourceEditMode){
43370             btn.toggle(this.sourceEditMode);
43371             return;
43372         }
43373         
43374         if(sourceEditMode){
43375             Roo.log("disabling buttons");
43376             this.tb.items.each(function(item){
43377                 if(item.cmd != 'sourceedit'){
43378                     item.disable();
43379                 }
43380             });
43381           
43382         }else{
43383             Roo.log("enabling buttons");
43384             if(this.editorcore.initialized){
43385                 this.tb.items.each(function(item){
43386                     item.enable();
43387                 });
43388             }
43389             
43390         }
43391         Roo.log("calling toggole on editor");
43392         // tell the editor that it's been pressed..
43393         this.editor.toggleSourceEdit(sourceEditMode);
43394        
43395     },
43396      /**
43397      * Object collection of toolbar tooltips for the buttons in the editor. The key
43398      * is the command id associated with that button and the value is a valid QuickTips object.
43399      * For example:
43400 <pre><code>
43401 {
43402     bold : {
43403         title: 'Bold (Ctrl+B)',
43404         text: 'Make the selected text bold.',
43405         cls: 'x-html-editor-tip'
43406     },
43407     italic : {
43408         title: 'Italic (Ctrl+I)',
43409         text: 'Make the selected text italic.',
43410         cls: 'x-html-editor-tip'
43411     },
43412     ...
43413 </code></pre>
43414     * @type Object
43415      */
43416     buttonTips : {
43417         bold : {
43418             title: 'Bold (Ctrl+B)',
43419             text: 'Make the selected text bold.',
43420             cls: 'x-html-editor-tip'
43421         },
43422         italic : {
43423             title: 'Italic (Ctrl+I)',
43424             text: 'Make the selected text italic.',
43425             cls: 'x-html-editor-tip'
43426         },
43427         underline : {
43428             title: 'Underline (Ctrl+U)',
43429             text: 'Underline the selected text.',
43430             cls: 'x-html-editor-tip'
43431         },
43432         increasefontsize : {
43433             title: 'Grow Text',
43434             text: 'Increase the font size.',
43435             cls: 'x-html-editor-tip'
43436         },
43437         decreasefontsize : {
43438             title: 'Shrink Text',
43439             text: 'Decrease the font size.',
43440             cls: 'x-html-editor-tip'
43441         },
43442         backcolor : {
43443             title: 'Text Highlight Color',
43444             text: 'Change the background color of the selected text.',
43445             cls: 'x-html-editor-tip'
43446         },
43447         forecolor : {
43448             title: 'Font Color',
43449             text: 'Change the color of the selected text.',
43450             cls: 'x-html-editor-tip'
43451         },
43452         justifyleft : {
43453             title: 'Align Text Left',
43454             text: 'Align text to the left.',
43455             cls: 'x-html-editor-tip'
43456         },
43457         justifycenter : {
43458             title: 'Center Text',
43459             text: 'Center text in the editor.',
43460             cls: 'x-html-editor-tip'
43461         },
43462         justifyright : {
43463             title: 'Align Text Right',
43464             text: 'Align text to the right.',
43465             cls: 'x-html-editor-tip'
43466         },
43467         insertunorderedlist : {
43468             title: 'Bullet List',
43469             text: 'Start a bulleted list.',
43470             cls: 'x-html-editor-tip'
43471         },
43472         insertorderedlist : {
43473             title: 'Numbered List',
43474             text: 'Start a numbered list.',
43475             cls: 'x-html-editor-tip'
43476         },
43477         createlink : {
43478             title: 'Hyperlink',
43479             text: 'Make the selected text a hyperlink.',
43480             cls: 'x-html-editor-tip'
43481         },
43482         sourceedit : {
43483             title: 'Source Edit',
43484             text: 'Switch to source editing mode.',
43485             cls: 'x-html-editor-tip'
43486         }
43487     },
43488     // private
43489     onDestroy : function(){
43490         if(this.rendered){
43491             
43492             this.tb.items.each(function(item){
43493                 if(item.menu){
43494                     item.menu.removeAll();
43495                     if(item.menu.el){
43496                         item.menu.el.destroy();
43497                     }
43498                 }
43499                 item.destroy();
43500             });
43501              
43502         }
43503     },
43504     onFirstFocus: function() {
43505         this.tb.items.each(function(item){
43506            item.enable();
43507         });
43508     }
43509 });
43510
43511
43512
43513
43514 // <script type="text/javascript">
43515 /*
43516  * Based on
43517  * Ext JS Library 1.1.1
43518  * Copyright(c) 2006-2007, Ext JS, LLC.
43519  *  
43520  
43521  */
43522
43523  
43524 /**
43525  * @class Roo.form.HtmlEditor.ToolbarContext
43526  * Context Toolbar
43527  * 
43528  * Usage:
43529  *
43530  new Roo.form.HtmlEditor({
43531     ....
43532     toolbars : [
43533         { xtype: 'ToolbarStandard', styles : {} }
43534         { xtype: 'ToolbarContext', disable : {} }
43535     ]
43536 })
43537
43538      
43539  * 
43540  * @config : {Object} disable List of elements to disable.. (not done yet.)
43541  * @config : {Object} styles  Map of styles available.
43542  * 
43543  */
43544
43545 Roo.form.HtmlEditor.ToolbarContext = function(config)
43546 {
43547     
43548     Roo.apply(this, config);
43549     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43550     // dont call parent... till later.
43551     this.styles = this.styles || {};
43552 }
43553
43554  
43555
43556 Roo.form.HtmlEditor.ToolbarContext.types = {
43557     'IMG' : {
43558         width : {
43559             title: "Width",
43560             width: 40
43561         },
43562         height:  {
43563             title: "Height",
43564             width: 40
43565         },
43566         align: {
43567             title: "Align",
43568             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43569             width : 80
43570             
43571         },
43572         border: {
43573             title: "Border",
43574             width: 40
43575         },
43576         alt: {
43577             title: "Alt",
43578             width: 120
43579         },
43580         src : {
43581             title: "Src",
43582             width: 220
43583         }
43584         
43585     },
43586     'A' : {
43587         name : {
43588             title: "Name",
43589             width: 50
43590         },
43591         target:  {
43592             title: "Target",
43593             width: 120
43594         },
43595         href:  {
43596             title: "Href",
43597             width: 220
43598         } // border?
43599         
43600     },
43601     'TABLE' : {
43602         rows : {
43603             title: "Rows",
43604             width: 20
43605         },
43606         cols : {
43607             title: "Cols",
43608             width: 20
43609         },
43610         width : {
43611             title: "Width",
43612             width: 40
43613         },
43614         height : {
43615             title: "Height",
43616             width: 40
43617         },
43618         border : {
43619             title: "Border",
43620             width: 20
43621         }
43622     },
43623     'TD' : {
43624         width : {
43625             title: "Width",
43626             width: 40
43627         },
43628         height : {
43629             title: "Height",
43630             width: 40
43631         },   
43632         align: {
43633             title: "Align",
43634             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43635             width: 80
43636         },
43637         valign: {
43638             title: "Valign",
43639             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43640             width: 80
43641         },
43642         colspan: {
43643             title: "Colspan",
43644             width: 20
43645             
43646         },
43647          'font-family'  : {
43648             title : "Font",
43649             style : 'fontFamily',
43650             displayField: 'display',
43651             optname : 'font-family',
43652             width: 140
43653         }
43654     },
43655     'INPUT' : {
43656         name : {
43657             title: "name",
43658             width: 120
43659         },
43660         value : {
43661             title: "Value",
43662             width: 120
43663         },
43664         width : {
43665             title: "Width",
43666             width: 40
43667         }
43668     },
43669     'LABEL' : {
43670         'for' : {
43671             title: "For",
43672             width: 120
43673         }
43674     },
43675     'TEXTAREA' : {
43676           name : {
43677             title: "name",
43678             width: 120
43679         },
43680         rows : {
43681             title: "Rows",
43682             width: 20
43683         },
43684         cols : {
43685             title: "Cols",
43686             width: 20
43687         }
43688     },
43689     'SELECT' : {
43690         name : {
43691             title: "name",
43692             width: 120
43693         },
43694         selectoptions : {
43695             title: "Options",
43696             width: 200
43697         }
43698     },
43699     
43700     // should we really allow this??
43701     // should this just be 
43702     'BODY' : {
43703         title : {
43704             title: "Title",
43705             width: 200,
43706             disabled : true
43707         }
43708     },
43709     'SPAN' : {
43710         'font-family'  : {
43711             title : "Font",
43712             style : 'fontFamily',
43713             displayField: 'display',
43714             optname : 'font-family',
43715             width: 140
43716         }
43717     },
43718     'DIV' : {
43719         'font-family'  : {
43720             title : "Font",
43721             style : 'fontFamily',
43722             displayField: 'display',
43723             optname : 'font-family',
43724             width: 140
43725         }
43726     },
43727      'P' : {
43728         'font-family'  : {
43729             title : "Font",
43730             style : 'fontFamily',
43731             displayField: 'display',
43732             optname : 'font-family',
43733             width: 140
43734         }
43735     },
43736     
43737     '*' : {
43738         // empty..
43739     }
43740
43741 };
43742
43743 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43744 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43745
43746 Roo.form.HtmlEditor.ToolbarContext.options = {
43747         'font-family'  : [ 
43748                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43749                 [ 'Courier New', 'Courier New'],
43750                 [ 'Tahoma', 'Tahoma'],
43751                 [ 'Times New Roman,serif', 'Times'],
43752                 [ 'Verdana','Verdana' ]
43753         ]
43754 };
43755
43756 // fixme - these need to be configurable..
43757  
43758
43759 Roo.form.HtmlEditor.ToolbarContext.types
43760
43761
43762 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43763     
43764     tb: false,
43765     
43766     rendered: false,
43767     
43768     editor : false,
43769     editorcore : false,
43770     /**
43771      * @cfg {Object} disable  List of toolbar elements to disable
43772          
43773      */
43774     disable : false,
43775     /**
43776      * @cfg {Object} styles List of styles 
43777      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43778      *
43779      * These must be defined in the page, so they get rendered correctly..
43780      * .headline { }
43781      * TD.underline { }
43782      * 
43783      */
43784     styles : false,
43785     
43786     options: false,
43787     
43788     toolbars : false,
43789     
43790     init : function(editor)
43791     {
43792         this.editor = editor;
43793         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43794         var editorcore = this.editorcore;
43795         
43796         var fid = editorcore.frameId;
43797         var etb = this;
43798         function btn(id, toggle, handler){
43799             var xid = fid + '-'+ id ;
43800             return {
43801                 id : xid,
43802                 cmd : id,
43803                 cls : 'x-btn-icon x-edit-'+id,
43804                 enableToggle:toggle !== false,
43805                 scope: editorcore, // was editor...
43806                 handler:handler||editorcore.relayBtnCmd,
43807                 clickEvent:'mousedown',
43808                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43809                 tabIndex:-1
43810             };
43811         }
43812         // create a new element.
43813         var wdiv = editor.wrap.createChild({
43814                 tag: 'div'
43815             }, editor.wrap.dom.firstChild.nextSibling, true);
43816         
43817         // can we do this more than once??
43818         
43819          // stop form submits
43820       
43821  
43822         // disable everything...
43823         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43824         this.toolbars = {};
43825            
43826         for (var i in  ty) {
43827           
43828             this.toolbars[i] = this.buildToolbar(ty[i],i);
43829         }
43830         this.tb = this.toolbars.BODY;
43831         this.tb.el.show();
43832         this.buildFooter();
43833         this.footer.show();
43834         editor.on('hide', function( ) { this.footer.hide() }, this);
43835         editor.on('show', function( ) { this.footer.show() }, this);
43836         
43837          
43838         this.rendered = true;
43839         
43840         // the all the btns;
43841         editor.on('editorevent', this.updateToolbar, this);
43842         // other toolbars need to implement this..
43843         //editor.on('editmodechange', this.updateToolbar, this);
43844     },
43845     
43846     
43847     
43848     /**
43849      * Protected method that will not generally be called directly. It triggers
43850      * a toolbar update by reading the markup state of the current selection in the editor.
43851      */
43852     updateToolbar: function(editor,ev,sel){
43853
43854         //Roo.log(ev);
43855         // capture mouse up - this is handy for selecting images..
43856         // perhaps should go somewhere else...
43857         if(!this.editorcore.activated){
43858              this.editor.onFirstFocus();
43859             return;
43860         }
43861         
43862         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43863         // selectNode - might want to handle IE?
43864         if (ev &&
43865             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43866             ev.target && ev.target.tagName == 'IMG') {
43867             // they have click on an image...
43868             // let's see if we can change the selection...
43869             sel = ev.target;
43870          
43871               var nodeRange = sel.ownerDocument.createRange();
43872             try {
43873                 nodeRange.selectNode(sel);
43874             } catch (e) {
43875                 nodeRange.selectNodeContents(sel);
43876             }
43877             //nodeRange.collapse(true);
43878             var s = this.editorcore.win.getSelection();
43879             s.removeAllRanges();
43880             s.addRange(nodeRange);
43881         }  
43882         
43883       
43884         var updateFooter = sel ? false : true;
43885         
43886         
43887         var ans = this.editorcore.getAllAncestors();
43888         
43889         // pick
43890         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43891         
43892         if (!sel) { 
43893             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43894             sel = sel ? sel : this.editorcore.doc.body;
43895             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43896             
43897         }
43898         // pick a menu that exists..
43899         var tn = sel.tagName.toUpperCase();
43900         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43901         
43902         tn = sel.tagName.toUpperCase();
43903         
43904         var lastSel = this.tb.selectedNode
43905         
43906         this.tb.selectedNode = sel;
43907         
43908         // if current menu does not match..
43909         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43910                 
43911             this.tb.el.hide();
43912             ///console.log("show: " + tn);
43913             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43914             this.tb.el.show();
43915             // update name
43916             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43917             
43918             
43919             // update attributes
43920             if (this.tb.fields) {
43921                 this.tb.fields.each(function(e) {
43922                     if (e.stylename) {
43923                         e.setValue(sel.style[e.stylename]);
43924                         return;
43925                     } 
43926                    e.setValue(sel.getAttribute(e.attrname));
43927                 });
43928             }
43929             
43930             var hasStyles = false;
43931             for(var i in this.styles) {
43932                 hasStyles = true;
43933                 break;
43934             }
43935             
43936             // update styles
43937             if (hasStyles) { 
43938                 var st = this.tb.fields.item(0);
43939                 
43940                 st.store.removeAll();
43941                
43942                 
43943                 var cn = sel.className.split(/\s+/);
43944                 
43945                 var avs = [];
43946                 if (this.styles['*']) {
43947                     
43948                     Roo.each(this.styles['*'], function(v) {
43949                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43950                     });
43951                 }
43952                 if (this.styles[tn]) { 
43953                     Roo.each(this.styles[tn], function(v) {
43954                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43955                     });
43956                 }
43957                 
43958                 st.store.loadData(avs);
43959                 st.collapse();
43960                 st.setValue(cn);
43961             }
43962             // flag our selected Node.
43963             this.tb.selectedNode = sel;
43964            
43965            
43966             Roo.menu.MenuMgr.hideAll();
43967
43968         }
43969         
43970         if (!updateFooter) {
43971             //this.footDisp.dom.innerHTML = ''; 
43972             return;
43973         }
43974         // update the footer
43975         //
43976         var html = '';
43977         
43978         this.footerEls = ans.reverse();
43979         Roo.each(this.footerEls, function(a,i) {
43980             if (!a) { return; }
43981             html += html.length ? ' &gt; '  :  '';
43982             
43983             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43984             
43985         });
43986        
43987         // 
43988         var sz = this.footDisp.up('td').getSize();
43989         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43990         this.footDisp.dom.style.marginLeft = '5px';
43991         
43992         this.footDisp.dom.style.overflow = 'hidden';
43993         
43994         this.footDisp.dom.innerHTML = html;
43995             
43996         //this.editorsyncValue();
43997     },
43998      
43999     
44000    
44001        
44002     // private
44003     onDestroy : function(){
44004         if(this.rendered){
44005             
44006             this.tb.items.each(function(item){
44007                 if(item.menu){
44008                     item.menu.removeAll();
44009                     if(item.menu.el){
44010                         item.menu.el.destroy();
44011                     }
44012                 }
44013                 item.destroy();
44014             });
44015              
44016         }
44017     },
44018     onFirstFocus: function() {
44019         // need to do this for all the toolbars..
44020         this.tb.items.each(function(item){
44021            item.enable();
44022         });
44023     },
44024     buildToolbar: function(tlist, nm)
44025     {
44026         var editor = this.editor;
44027         var editorcore = this.editorcore;
44028          // create a new element.
44029         var wdiv = editor.wrap.createChild({
44030                 tag: 'div'
44031             }, editor.wrap.dom.firstChild.nextSibling, true);
44032         
44033        
44034         var tb = new Roo.Toolbar(wdiv);
44035         // add the name..
44036         
44037         tb.add(nm+ ":&nbsp;");
44038         
44039         var styles = [];
44040         for(var i in this.styles) {
44041             styles.push(i);
44042         }
44043         
44044         // styles...
44045         if (styles && styles.length) {
44046             
44047             // this needs a multi-select checkbox...
44048             tb.addField( new Roo.form.ComboBox({
44049                 store: new Roo.data.SimpleStore({
44050                     id : 'val',
44051                     fields: ['val', 'selected'],
44052                     data : [] 
44053                 }),
44054                 name : '-roo-edit-className',
44055                 attrname : 'className',
44056                 displayField: 'val',
44057                 typeAhead: false,
44058                 mode: 'local',
44059                 editable : false,
44060                 triggerAction: 'all',
44061                 emptyText:'Select Style',
44062                 selectOnFocus:true,
44063                 width: 130,
44064                 listeners : {
44065                     'select': function(c, r, i) {
44066                         // initial support only for on class per el..
44067                         tb.selectedNode.className =  r ? r.get('val') : '';
44068                         editorcore.syncValue();
44069                     }
44070                 }
44071     
44072             }));
44073         }
44074         
44075         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44076         var tbops = tbc.options;
44077         
44078         for (var i in tlist) {
44079             
44080             var item = tlist[i];
44081             tb.add(item.title + ":&nbsp;");
44082             
44083             
44084             //optname == used so you can configure the options available..
44085             var opts = item.opts ? item.opts : false;
44086             if (item.optname) {
44087                 opts = tbops[item.optname];
44088            
44089             }
44090             
44091             if (opts) {
44092                 // opts == pulldown..
44093                 tb.addField( new Roo.form.ComboBox({
44094                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44095                         id : 'val',
44096                         fields: ['val', 'display'],
44097                         data : opts  
44098                     }),
44099                     name : '-roo-edit-' + i,
44100                     attrname : i,
44101                     stylename : item.style ? item.style : false,
44102                     displayField: item.displayField ? item.displayField : 'val',
44103                     valueField :  'val',
44104                     typeAhead: false,
44105                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44106                     editable : false,
44107                     triggerAction: 'all',
44108                     emptyText:'Select',
44109                     selectOnFocus:true,
44110                     width: item.width ? item.width  : 130,
44111                     listeners : {
44112                         'select': function(c, r, i) {
44113                             if (c.stylename) {
44114                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44115                                 return;
44116                             }
44117                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44118                         }
44119                     }
44120
44121                 }));
44122                 continue;
44123                     
44124                  
44125                 
44126                 tb.addField( new Roo.form.TextField({
44127                     name: i,
44128                     width: 100,
44129                     //allowBlank:false,
44130                     value: ''
44131                 }));
44132                 continue;
44133             }
44134             tb.addField( new Roo.form.TextField({
44135                 name: '-roo-edit-' + i,
44136                 attrname : i,
44137                 
44138                 width: item.width,
44139                 //allowBlank:true,
44140                 value: '',
44141                 listeners: {
44142                     'change' : function(f, nv, ov) {
44143                         tb.selectedNode.setAttribute(f.attrname, nv);
44144                     }
44145                 }
44146             }));
44147              
44148         }
44149         tb.addFill();
44150         var _this = this;
44151         tb.addButton( {
44152             text: 'Remove Tag',
44153     
44154             listeners : {
44155                 click : function ()
44156                 {
44157                     // remove
44158                     // undo does not work.
44159                      
44160                     var sn = tb.selectedNode;
44161                     
44162                     var pn = sn.parentNode;
44163                     
44164                     var stn =  sn.childNodes[0];
44165                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44166                     while (sn.childNodes.length) {
44167                         var node = sn.childNodes[0];
44168                         sn.removeChild(node);
44169                         //Roo.log(node);
44170                         pn.insertBefore(node, sn);
44171                         
44172                     }
44173                     pn.removeChild(sn);
44174                     var range = editorcore.createRange();
44175         
44176                     range.setStart(stn,0);
44177                     range.setEnd(en,0); //????
44178                     //range.selectNode(sel);
44179                     
44180                     
44181                     var selection = editorcore.getSelection();
44182                     selection.removeAllRanges();
44183                     selection.addRange(range);
44184                     
44185                     
44186                     
44187                     //_this.updateToolbar(null, null, pn);
44188                     _this.updateToolbar(null, null, null);
44189                     _this.footDisp.dom.innerHTML = ''; 
44190                 }
44191             }
44192             
44193                     
44194                 
44195             
44196         });
44197         
44198         
44199         tb.el.on('click', function(e){
44200             e.preventDefault(); // what does this do?
44201         });
44202         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44203         tb.el.hide();
44204         tb.name = nm;
44205         // dont need to disable them... as they will get hidden
44206         return tb;
44207          
44208         
44209     },
44210     buildFooter : function()
44211     {
44212         
44213         var fel = this.editor.wrap.createChild();
44214         this.footer = new Roo.Toolbar(fel);
44215         // toolbar has scrolly on left / right?
44216         var footDisp= new Roo.Toolbar.Fill();
44217         var _t = this;
44218         this.footer.add(
44219             {
44220                 text : '&lt;',
44221                 xtype: 'Button',
44222                 handler : function() {
44223                     _t.footDisp.scrollTo('left',0,true)
44224                 }
44225             }
44226         );
44227         this.footer.add( footDisp );
44228         this.footer.add( 
44229             {
44230                 text : '&gt;',
44231                 xtype: 'Button',
44232                 handler : function() {
44233                     // no animation..
44234                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44235                 }
44236             }
44237         );
44238         var fel = Roo.get(footDisp.el);
44239         fel.addClass('x-editor-context');
44240         this.footDispWrap = fel; 
44241         this.footDispWrap.overflow  = 'hidden';
44242         
44243         this.footDisp = fel.createChild();
44244         this.footDispWrap.on('click', this.onContextClick, this)
44245         
44246         
44247     },
44248     onContextClick : function (ev,dom)
44249     {
44250         ev.preventDefault();
44251         var  cn = dom.className;
44252         //Roo.log(cn);
44253         if (!cn.match(/x-ed-loc-/)) {
44254             return;
44255         }
44256         var n = cn.split('-').pop();
44257         var ans = this.footerEls;
44258         var sel = ans[n];
44259         
44260          // pick
44261         var range = this.editorcore.createRange();
44262         
44263         range.selectNodeContents(sel);
44264         //range.selectNode(sel);
44265         
44266         
44267         var selection = this.editorcore.getSelection();
44268         selection.removeAllRanges();
44269         selection.addRange(range);
44270         
44271         
44272         
44273         this.updateToolbar(null, null, sel);
44274         
44275         
44276     }
44277     
44278     
44279     
44280     
44281     
44282 });
44283
44284
44285
44286
44287
44288 /*
44289  * Based on:
44290  * Ext JS Library 1.1.1
44291  * Copyright(c) 2006-2007, Ext JS, LLC.
44292  *
44293  * Originally Released Under LGPL - original licence link has changed is not relivant.
44294  *
44295  * Fork - LGPL
44296  * <script type="text/javascript">
44297  */
44298  
44299 /**
44300  * @class Roo.form.BasicForm
44301  * @extends Roo.util.Observable
44302  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44303  * @constructor
44304  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44305  * @param {Object} config Configuration options
44306  */
44307 Roo.form.BasicForm = function(el, config){
44308     this.allItems = [];
44309     this.childForms = [];
44310     Roo.apply(this, config);
44311     /*
44312      * The Roo.form.Field items in this form.
44313      * @type MixedCollection
44314      */
44315      
44316      
44317     this.items = new Roo.util.MixedCollection(false, function(o){
44318         return o.id || (o.id = Roo.id());
44319     });
44320     this.addEvents({
44321         /**
44322          * @event beforeaction
44323          * Fires before any action is performed. Return false to cancel the action.
44324          * @param {Form} this
44325          * @param {Action} action The action to be performed
44326          */
44327         beforeaction: true,
44328         /**
44329          * @event actionfailed
44330          * Fires when an action fails.
44331          * @param {Form} this
44332          * @param {Action} action The action that failed
44333          */
44334         actionfailed : true,
44335         /**
44336          * @event actioncomplete
44337          * Fires when an action is completed.
44338          * @param {Form} this
44339          * @param {Action} action The action that completed
44340          */
44341         actioncomplete : true
44342     });
44343     if(el){
44344         this.initEl(el);
44345     }
44346     Roo.form.BasicForm.superclass.constructor.call(this);
44347 };
44348
44349 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44350     /**
44351      * @cfg {String} method
44352      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44353      */
44354     /**
44355      * @cfg {DataReader} reader
44356      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44357      * This is optional as there is built-in support for processing JSON.
44358      */
44359     /**
44360      * @cfg {DataReader} errorReader
44361      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44362      * This is completely optional as there is built-in support for processing JSON.
44363      */
44364     /**
44365      * @cfg {String} url
44366      * The URL to use for form actions if one isn't supplied in the action options.
44367      */
44368     /**
44369      * @cfg {Boolean} fileUpload
44370      * Set to true if this form is a file upload.
44371      */
44372      
44373     /**
44374      * @cfg {Object} baseParams
44375      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44376      */
44377      /**
44378      
44379     /**
44380      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44381      */
44382     timeout: 30,
44383
44384     // private
44385     activeAction : null,
44386
44387     /**
44388      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44389      * or setValues() data instead of when the form was first created.
44390      */
44391     trackResetOnLoad : false,
44392     
44393     
44394     /**
44395      * childForms - used for multi-tab forms
44396      * @type {Array}
44397      */
44398     childForms : false,
44399     
44400     /**
44401      * allItems - full list of fields.
44402      * @type {Array}
44403      */
44404     allItems : false,
44405     
44406     /**
44407      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44408      * element by passing it or its id or mask the form itself by passing in true.
44409      * @type Mixed
44410      */
44411     waitMsgTarget : false,
44412
44413     // private
44414     initEl : function(el){
44415         this.el = Roo.get(el);
44416         this.id = this.el.id || Roo.id();
44417         this.el.on('submit', this.onSubmit, this);
44418         this.el.addClass('x-form');
44419     },
44420
44421     // private
44422     onSubmit : function(e){
44423         e.stopEvent();
44424     },
44425
44426     /**
44427      * Returns true if client-side validation on the form is successful.
44428      * @return Boolean
44429      */
44430     isValid : function(){
44431         var valid = true;
44432         this.items.each(function(f){
44433            if(!f.validate()){
44434                valid = false;
44435            }
44436         });
44437         return valid;
44438     },
44439
44440     /**
44441      * Returns true if any fields in this form have changed since their original load.
44442      * @return Boolean
44443      */
44444     isDirty : function(){
44445         var dirty = false;
44446         this.items.each(function(f){
44447            if(f.isDirty()){
44448                dirty = true;
44449                return false;
44450            }
44451         });
44452         return dirty;
44453     },
44454
44455     /**
44456      * Performs a predefined action (submit or load) or custom actions you define on this form.
44457      * @param {String} actionName The name of the action type
44458      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44459      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44460      * accept other config options):
44461      * <pre>
44462 Property          Type             Description
44463 ----------------  ---------------  ----------------------------------------------------------------------------------
44464 url               String           The url for the action (defaults to the form's url)
44465 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44466 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44467 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44468                                    validate the form on the client (defaults to false)
44469      * </pre>
44470      * @return {BasicForm} this
44471      */
44472     doAction : function(action, options){
44473         if(typeof action == 'string'){
44474             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44475         }
44476         if(this.fireEvent('beforeaction', this, action) !== false){
44477             this.beforeAction(action);
44478             action.run.defer(100, action);
44479         }
44480         return this;
44481     },
44482
44483     /**
44484      * Shortcut to do a submit action.
44485      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44486      * @return {BasicForm} this
44487      */
44488     submit : function(options){
44489         this.doAction('submit', options);
44490         return this;
44491     },
44492
44493     /**
44494      * Shortcut to do a load action.
44495      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44496      * @return {BasicForm} this
44497      */
44498     load : function(options){
44499         this.doAction('load', options);
44500         return this;
44501     },
44502
44503     /**
44504      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44505      * @param {Record} record The record to edit
44506      * @return {BasicForm} this
44507      */
44508     updateRecord : function(record){
44509         record.beginEdit();
44510         var fs = record.fields;
44511         fs.each(function(f){
44512             var field = this.findField(f.name);
44513             if(field){
44514                 record.set(f.name, field.getValue());
44515             }
44516         }, this);
44517         record.endEdit();
44518         return this;
44519     },
44520
44521     /**
44522      * Loads an Roo.data.Record into this form.
44523      * @param {Record} record The record to load
44524      * @return {BasicForm} this
44525      */
44526     loadRecord : function(record){
44527         this.setValues(record.data);
44528         return this;
44529     },
44530
44531     // private
44532     beforeAction : function(action){
44533         var o = action.options;
44534         
44535        
44536         if(this.waitMsgTarget === true){
44537             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44538         }else if(this.waitMsgTarget){
44539             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44540             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44541         }else {
44542             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44543         }
44544          
44545     },
44546
44547     // private
44548     afterAction : function(action, success){
44549         this.activeAction = null;
44550         var o = action.options;
44551         
44552         if(this.waitMsgTarget === true){
44553             this.el.unmask();
44554         }else if(this.waitMsgTarget){
44555             this.waitMsgTarget.unmask();
44556         }else{
44557             Roo.MessageBox.updateProgress(1);
44558             Roo.MessageBox.hide();
44559         }
44560          
44561         if(success){
44562             if(o.reset){
44563                 this.reset();
44564             }
44565             Roo.callback(o.success, o.scope, [this, action]);
44566             this.fireEvent('actioncomplete', this, action);
44567             
44568         }else{
44569             
44570             // failure condition..
44571             // we have a scenario where updates need confirming.
44572             // eg. if a locking scenario exists..
44573             // we look for { errors : { needs_confirm : true }} in the response.
44574             if (
44575                 (typeof(action.result) != 'undefined')  &&
44576                 (typeof(action.result.errors) != 'undefined')  &&
44577                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44578            ){
44579                 var _t = this;
44580                 Roo.MessageBox.confirm(
44581                     "Change requires confirmation",
44582                     action.result.errorMsg,
44583                     function(r) {
44584                         if (r != 'yes') {
44585                             return;
44586                         }
44587                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44588                     }
44589                     
44590                 );
44591                 
44592                 
44593                 
44594                 return;
44595             }
44596             
44597             Roo.callback(o.failure, o.scope, [this, action]);
44598             // show an error message if no failed handler is set..
44599             if (!this.hasListener('actionfailed')) {
44600                 Roo.MessageBox.alert("Error",
44601                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44602                         action.result.errorMsg :
44603                         "Saving Failed, please check your entries or try again"
44604                 );
44605             }
44606             
44607             this.fireEvent('actionfailed', this, action);
44608         }
44609         
44610     },
44611
44612     /**
44613      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44614      * @param {String} id The value to search for
44615      * @return Field
44616      */
44617     findField : function(id){
44618         var field = this.items.get(id);
44619         if(!field){
44620             this.items.each(function(f){
44621                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44622                     field = f;
44623                     return false;
44624                 }
44625             });
44626         }
44627         return field || null;
44628     },
44629
44630     /**
44631      * Add a secondary form to this one, 
44632      * Used to provide tabbed forms. One form is primary, with hidden values 
44633      * which mirror the elements from the other forms.
44634      * 
44635      * @param {Roo.form.Form} form to add.
44636      * 
44637      */
44638     addForm : function(form)
44639     {
44640        
44641         if (this.childForms.indexOf(form) > -1) {
44642             // already added..
44643             return;
44644         }
44645         this.childForms.push(form);
44646         var n = '';
44647         Roo.each(form.allItems, function (fe) {
44648             
44649             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44650             if (this.findField(n)) { // already added..
44651                 return;
44652             }
44653             var add = new Roo.form.Hidden({
44654                 name : n
44655             });
44656             add.render(this.el);
44657             
44658             this.add( add );
44659         }, this);
44660         
44661     },
44662     /**
44663      * Mark fields in this form invalid in bulk.
44664      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44665      * @return {BasicForm} this
44666      */
44667     markInvalid : function(errors){
44668         if(errors instanceof Array){
44669             for(var i = 0, len = errors.length; i < len; i++){
44670                 var fieldError = errors[i];
44671                 var f = this.findField(fieldError.id);
44672                 if(f){
44673                     f.markInvalid(fieldError.msg);
44674                 }
44675             }
44676         }else{
44677             var field, id;
44678             for(id in errors){
44679                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44680                     field.markInvalid(errors[id]);
44681                 }
44682             }
44683         }
44684         Roo.each(this.childForms || [], function (f) {
44685             f.markInvalid(errors);
44686         });
44687         
44688         return this;
44689     },
44690
44691     /**
44692      * Set values for fields in this form in bulk.
44693      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44694      * @return {BasicForm} this
44695      */
44696     setValues : function(values){
44697         if(values instanceof Array){ // array of objects
44698             for(var i = 0, len = values.length; i < len; i++){
44699                 var v = values[i];
44700                 var f = this.findField(v.id);
44701                 if(f){
44702                     f.setValue(v.value);
44703                     if(this.trackResetOnLoad){
44704                         f.originalValue = f.getValue();
44705                     }
44706                 }
44707             }
44708         }else{ // object hash
44709             var field, id;
44710             for(id in values){
44711                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44712                     
44713                     if (field.setFromData && 
44714                         field.valueField && 
44715                         field.displayField &&
44716                         // combos' with local stores can 
44717                         // be queried via setValue()
44718                         // to set their value..
44719                         (field.store && !field.store.isLocal)
44720                         ) {
44721                         // it's a combo
44722                         var sd = { };
44723                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44724                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44725                         field.setFromData(sd);
44726                         
44727                     } else {
44728                         field.setValue(values[id]);
44729                     }
44730                     
44731                     
44732                     if(this.trackResetOnLoad){
44733                         field.originalValue = field.getValue();
44734                     }
44735                 }
44736             }
44737         }
44738          
44739         Roo.each(this.childForms || [], function (f) {
44740             f.setValues(values);
44741         });
44742                 
44743         return this;
44744     },
44745
44746     /**
44747      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44748      * they are returned as an array.
44749      * @param {Boolean} asString
44750      * @return {Object}
44751      */
44752     getValues : function(asString){
44753         if (this.childForms) {
44754             // copy values from the child forms
44755             Roo.each(this.childForms, function (f) {
44756                 this.setValues(f.getValues());
44757             }, this);
44758         }
44759         
44760         
44761         
44762         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44763         if(asString === true){
44764             return fs;
44765         }
44766         return Roo.urlDecode(fs);
44767     },
44768     
44769     /**
44770      * Returns the fields in this form as an object with key/value pairs. 
44771      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44772      * @return {Object}
44773      */
44774     getFieldValues : function(with_hidden)
44775     {
44776         if (this.childForms) {
44777             // copy values from the child forms
44778             // should this call getFieldValues - probably not as we do not currently copy
44779             // hidden fields when we generate..
44780             Roo.each(this.childForms, function (f) {
44781                 this.setValues(f.getValues());
44782             }, this);
44783         }
44784         
44785         var ret = {};
44786         this.items.each(function(f){
44787             if (!f.getName()) {
44788                 return;
44789             }
44790             var v = f.getValue();
44791             if (f.inputType =='radio') {
44792                 if (typeof(ret[f.getName()]) == 'undefined') {
44793                     ret[f.getName()] = ''; // empty..
44794                 }
44795                 
44796                 if (!f.el.dom.checked) {
44797                     return;
44798                     
44799                 }
44800                 v = f.el.dom.value;
44801                 
44802             }
44803             
44804             // not sure if this supported any more..
44805             if ((typeof(v) == 'object') && f.getRawValue) {
44806                 v = f.getRawValue() ; // dates..
44807             }
44808             // combo boxes where name != hiddenName...
44809             if (f.name != f.getName()) {
44810                 ret[f.name] = f.getRawValue();
44811             }
44812             ret[f.getName()] = v;
44813         });
44814         
44815         return ret;
44816     },
44817
44818     /**
44819      * Clears all invalid messages in this form.
44820      * @return {BasicForm} this
44821      */
44822     clearInvalid : function(){
44823         this.items.each(function(f){
44824            f.clearInvalid();
44825         });
44826         
44827         Roo.each(this.childForms || [], function (f) {
44828             f.clearInvalid();
44829         });
44830         
44831         
44832         return this;
44833     },
44834
44835     /**
44836      * Resets this form.
44837      * @return {BasicForm} this
44838      */
44839     reset : function(){
44840         this.items.each(function(f){
44841             f.reset();
44842         });
44843         
44844         Roo.each(this.childForms || [], function (f) {
44845             f.reset();
44846         });
44847        
44848         
44849         return this;
44850     },
44851
44852     /**
44853      * Add Roo.form components to this form.
44854      * @param {Field} field1
44855      * @param {Field} field2 (optional)
44856      * @param {Field} etc (optional)
44857      * @return {BasicForm} this
44858      */
44859     add : function(){
44860         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44861         return this;
44862     },
44863
44864
44865     /**
44866      * Removes a field from the items collection (does NOT remove its markup).
44867      * @param {Field} field
44868      * @return {BasicForm} this
44869      */
44870     remove : function(field){
44871         this.items.remove(field);
44872         return this;
44873     },
44874
44875     /**
44876      * Looks at the fields in this form, checks them for an id attribute,
44877      * and calls applyTo on the existing dom element with that id.
44878      * @return {BasicForm} this
44879      */
44880     render : function(){
44881         this.items.each(function(f){
44882             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44883                 f.applyTo(f.id);
44884             }
44885         });
44886         return this;
44887     },
44888
44889     /**
44890      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44891      * @param {Object} values
44892      * @return {BasicForm} this
44893      */
44894     applyToFields : function(o){
44895         this.items.each(function(f){
44896            Roo.apply(f, o);
44897         });
44898         return this;
44899     },
44900
44901     /**
44902      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44903      * @param {Object} values
44904      * @return {BasicForm} this
44905      */
44906     applyIfToFields : function(o){
44907         this.items.each(function(f){
44908            Roo.applyIf(f, o);
44909         });
44910         return this;
44911     }
44912 });
44913
44914 // back compat
44915 Roo.BasicForm = Roo.form.BasicForm;/*
44916  * Based on:
44917  * Ext JS Library 1.1.1
44918  * Copyright(c) 2006-2007, Ext JS, LLC.
44919  *
44920  * Originally Released Under LGPL - original licence link has changed is not relivant.
44921  *
44922  * Fork - LGPL
44923  * <script type="text/javascript">
44924  */
44925
44926 /**
44927  * @class Roo.form.Form
44928  * @extends Roo.form.BasicForm
44929  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44930  * @constructor
44931  * @param {Object} config Configuration options
44932  */
44933 Roo.form.Form = function(config){
44934     var xitems =  [];
44935     if (config.items) {
44936         xitems = config.items;
44937         delete config.items;
44938     }
44939    
44940     
44941     Roo.form.Form.superclass.constructor.call(this, null, config);
44942     this.url = this.url || this.action;
44943     if(!this.root){
44944         this.root = new Roo.form.Layout(Roo.applyIf({
44945             id: Roo.id()
44946         }, config));
44947     }
44948     this.active = this.root;
44949     /**
44950      * Array of all the buttons that have been added to this form via {@link addButton}
44951      * @type Array
44952      */
44953     this.buttons = [];
44954     this.allItems = [];
44955     this.addEvents({
44956         /**
44957          * @event clientvalidation
44958          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44959          * @param {Form} this
44960          * @param {Boolean} valid true if the form has passed client-side validation
44961          */
44962         clientvalidation: true,
44963         /**
44964          * @event rendered
44965          * Fires when the form is rendered
44966          * @param {Roo.form.Form} form
44967          */
44968         rendered : true
44969     });
44970     
44971     if (this.progressUrl) {
44972             // push a hidden field onto the list of fields..
44973             this.addxtype( {
44974                     xns: Roo.form, 
44975                     xtype : 'Hidden', 
44976                     name : 'UPLOAD_IDENTIFIER' 
44977             });
44978         }
44979         
44980     
44981     Roo.each(xitems, this.addxtype, this);
44982     
44983     
44984     
44985 };
44986
44987 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44988     /**
44989      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44990      */
44991     /**
44992      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44993      */
44994     /**
44995      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44996      */
44997     buttonAlign:'center',
44998
44999     /**
45000      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45001      */
45002     minButtonWidth:75,
45003
45004     /**
45005      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45006      * This property cascades to child containers if not set.
45007      */
45008     labelAlign:'left',
45009
45010     /**
45011      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45012      * fires a looping event with that state. This is required to bind buttons to the valid
45013      * state using the config value formBind:true on the button.
45014      */
45015     monitorValid : false,
45016
45017     /**
45018      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45019      */
45020     monitorPoll : 200,
45021     
45022     /**
45023      * @cfg {String} progressUrl - Url to return progress data 
45024      */
45025     
45026     progressUrl : false,
45027   
45028     /**
45029      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45030      * fields are added and the column is closed. If no fields are passed the column remains open
45031      * until end() is called.
45032      * @param {Object} config The config to pass to the column
45033      * @param {Field} field1 (optional)
45034      * @param {Field} field2 (optional)
45035      * @param {Field} etc (optional)
45036      * @return Column The column container object
45037      */
45038     column : function(c){
45039         var col = new Roo.form.Column(c);
45040         this.start(col);
45041         if(arguments.length > 1){ // duplicate code required because of Opera
45042             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45043             this.end();
45044         }
45045         return col;
45046     },
45047
45048     /**
45049      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45050      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45051      * until end() is called.
45052      * @param {Object} config The config to pass to the fieldset
45053      * @param {Field} field1 (optional)
45054      * @param {Field} field2 (optional)
45055      * @param {Field} etc (optional)
45056      * @return FieldSet The fieldset container object
45057      */
45058     fieldset : function(c){
45059         var fs = new Roo.form.FieldSet(c);
45060         this.start(fs);
45061         if(arguments.length > 1){ // duplicate code required because of Opera
45062             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45063             this.end();
45064         }
45065         return fs;
45066     },
45067
45068     /**
45069      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45070      * fields are added and the container is closed. If no fields are passed the container remains open
45071      * until end() is called.
45072      * @param {Object} config The config to pass to the Layout
45073      * @param {Field} field1 (optional)
45074      * @param {Field} field2 (optional)
45075      * @param {Field} etc (optional)
45076      * @return Layout The container object
45077      */
45078     container : function(c){
45079         var l = new Roo.form.Layout(c);
45080         this.start(l);
45081         if(arguments.length > 1){ // duplicate code required because of Opera
45082             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45083             this.end();
45084         }
45085         return l;
45086     },
45087
45088     /**
45089      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45090      * @param {Object} container A Roo.form.Layout or subclass of Layout
45091      * @return {Form} this
45092      */
45093     start : function(c){
45094         // cascade label info
45095         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45096         this.active.stack.push(c);
45097         c.ownerCt = this.active;
45098         this.active = c;
45099         return this;
45100     },
45101
45102     /**
45103      * Closes the current open container
45104      * @return {Form} this
45105      */
45106     end : function(){
45107         if(this.active == this.root){
45108             return this;
45109         }
45110         this.active = this.active.ownerCt;
45111         return this;
45112     },
45113
45114     /**
45115      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45116      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45117      * as the label of the field.
45118      * @param {Field} field1
45119      * @param {Field} field2 (optional)
45120      * @param {Field} etc. (optional)
45121      * @return {Form} this
45122      */
45123     add : function(){
45124         this.active.stack.push.apply(this.active.stack, arguments);
45125         this.allItems.push.apply(this.allItems,arguments);
45126         var r = [];
45127         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45128             if(a[i].isFormField){
45129                 r.push(a[i]);
45130             }
45131         }
45132         if(r.length > 0){
45133             Roo.form.Form.superclass.add.apply(this, r);
45134         }
45135         return this;
45136     },
45137     
45138
45139     
45140     
45141     
45142      /**
45143      * Find any element that has been added to a form, using it's ID or name
45144      * This can include framesets, columns etc. along with regular fields..
45145      * @param {String} id - id or name to find.
45146      
45147      * @return {Element} e - or false if nothing found.
45148      */
45149     findbyId : function(id)
45150     {
45151         var ret = false;
45152         if (!id) {
45153             return ret;
45154         }
45155         Roo.each(this.allItems, function(f){
45156             if (f.id == id || f.name == id ){
45157                 ret = f;
45158                 return false;
45159             }
45160         });
45161         return ret;
45162     },
45163
45164     
45165     
45166     /**
45167      * Render this form into the passed container. This should only be called once!
45168      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45169      * @return {Form} this
45170      */
45171     render : function(ct)
45172     {
45173         
45174         
45175         
45176         ct = Roo.get(ct);
45177         var o = this.autoCreate || {
45178             tag: 'form',
45179             method : this.method || 'POST',
45180             id : this.id || Roo.id()
45181         };
45182         this.initEl(ct.createChild(o));
45183
45184         this.root.render(this.el);
45185         
45186        
45187              
45188         this.items.each(function(f){
45189             f.render('x-form-el-'+f.id);
45190         });
45191
45192         if(this.buttons.length > 0){
45193             // tables are required to maintain order and for correct IE layout
45194             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45195                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45196                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45197             }}, null, true);
45198             var tr = tb.getElementsByTagName('tr')[0];
45199             for(var i = 0, len = this.buttons.length; i < len; i++) {
45200                 var b = this.buttons[i];
45201                 var td = document.createElement('td');
45202                 td.className = 'x-form-btn-td';
45203                 b.render(tr.appendChild(td));
45204             }
45205         }
45206         if(this.monitorValid){ // initialize after render
45207             this.startMonitoring();
45208         }
45209         this.fireEvent('rendered', this);
45210         return this;
45211     },
45212
45213     /**
45214      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45215      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45216      * object or a valid Roo.DomHelper element config
45217      * @param {Function} handler The function called when the button is clicked
45218      * @param {Object} scope (optional) The scope of the handler function
45219      * @return {Roo.Button}
45220      */
45221     addButton : function(config, handler, scope){
45222         var bc = {
45223             handler: handler,
45224             scope: scope,
45225             minWidth: this.minButtonWidth,
45226             hideParent:true
45227         };
45228         if(typeof config == "string"){
45229             bc.text = config;
45230         }else{
45231             Roo.apply(bc, config);
45232         }
45233         var btn = new Roo.Button(null, bc);
45234         this.buttons.push(btn);
45235         return btn;
45236     },
45237
45238      /**
45239      * Adds a series of form elements (using the xtype property as the factory method.
45240      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45241      * @param {Object} config 
45242      */
45243     
45244     addxtype : function()
45245     {
45246         var ar = Array.prototype.slice.call(arguments, 0);
45247         var ret = false;
45248         for(var i = 0; i < ar.length; i++) {
45249             if (!ar[i]) {
45250                 continue; // skip -- if this happends something invalid got sent, we 
45251                 // should ignore it, as basically that interface element will not show up
45252                 // and that should be pretty obvious!!
45253             }
45254             
45255             if (Roo.form[ar[i].xtype]) {
45256                 ar[i].form = this;
45257                 var fe = Roo.factory(ar[i], Roo.form);
45258                 if (!ret) {
45259                     ret = fe;
45260                 }
45261                 fe.form = this;
45262                 if (fe.store) {
45263                     fe.store.form = this;
45264                 }
45265                 if (fe.isLayout) {  
45266                          
45267                     this.start(fe);
45268                     this.allItems.push(fe);
45269                     if (fe.items && fe.addxtype) {
45270                         fe.addxtype.apply(fe, fe.items);
45271                         delete fe.items;
45272                     }
45273                      this.end();
45274                     continue;
45275                 }
45276                 
45277                 
45278                  
45279                 this.add(fe);
45280               //  console.log('adding ' + ar[i].xtype);
45281             }
45282             if (ar[i].xtype == 'Button') {  
45283                 //console.log('adding button');
45284                 //console.log(ar[i]);
45285                 this.addButton(ar[i]);
45286                 this.allItems.push(fe);
45287                 continue;
45288             }
45289             
45290             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45291                 alert('end is not supported on xtype any more, use items');
45292             //    this.end();
45293             //    //console.log('adding end');
45294             }
45295             
45296         }
45297         return ret;
45298     },
45299     
45300     /**
45301      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45302      * option "monitorValid"
45303      */
45304     startMonitoring : function(){
45305         if(!this.bound){
45306             this.bound = true;
45307             Roo.TaskMgr.start({
45308                 run : this.bindHandler,
45309                 interval : this.monitorPoll || 200,
45310                 scope: this
45311             });
45312         }
45313     },
45314
45315     /**
45316      * Stops monitoring of the valid state of this form
45317      */
45318     stopMonitoring : function(){
45319         this.bound = false;
45320     },
45321
45322     // private
45323     bindHandler : function(){
45324         if(!this.bound){
45325             return false; // stops binding
45326         }
45327         var valid = true;
45328         this.items.each(function(f){
45329             if(!f.isValid(true)){
45330                 valid = false;
45331                 return false;
45332             }
45333         });
45334         for(var i = 0, len = this.buttons.length; i < len; i++){
45335             var btn = this.buttons[i];
45336             if(btn.formBind === true && btn.disabled === valid){
45337                 btn.setDisabled(!valid);
45338             }
45339         }
45340         this.fireEvent('clientvalidation', this, valid);
45341     }
45342     
45343     
45344     
45345     
45346     
45347     
45348     
45349     
45350 });
45351
45352
45353 // back compat
45354 Roo.Form = Roo.form.Form;
45355 /*
45356  * Based on:
45357  * Ext JS Library 1.1.1
45358  * Copyright(c) 2006-2007, Ext JS, LLC.
45359  *
45360  * Originally Released Under LGPL - original licence link has changed is not relivant.
45361  *
45362  * Fork - LGPL
45363  * <script type="text/javascript">
45364  */
45365
45366 // as we use this in bootstrap.
45367 Roo.namespace('Roo.form');
45368  /**
45369  * @class Roo.form.Action
45370  * Internal Class used to handle form actions
45371  * @constructor
45372  * @param {Roo.form.BasicForm} el The form element or its id
45373  * @param {Object} config Configuration options
45374  */
45375
45376  
45377  
45378 // define the action interface
45379 Roo.form.Action = function(form, options){
45380     this.form = form;
45381     this.options = options || {};
45382 };
45383 /**
45384  * Client Validation Failed
45385  * @const 
45386  */
45387 Roo.form.Action.CLIENT_INVALID = 'client';
45388 /**
45389  * Server Validation Failed
45390  * @const 
45391  */
45392 Roo.form.Action.SERVER_INVALID = 'server';
45393  /**
45394  * Connect to Server Failed
45395  * @const 
45396  */
45397 Roo.form.Action.CONNECT_FAILURE = 'connect';
45398 /**
45399  * Reading Data from Server Failed
45400  * @const 
45401  */
45402 Roo.form.Action.LOAD_FAILURE = 'load';
45403
45404 Roo.form.Action.prototype = {
45405     type : 'default',
45406     failureType : undefined,
45407     response : undefined,
45408     result : undefined,
45409
45410     // interface method
45411     run : function(options){
45412
45413     },
45414
45415     // interface method
45416     success : function(response){
45417
45418     },
45419
45420     // interface method
45421     handleResponse : function(response){
45422
45423     },
45424
45425     // default connection failure
45426     failure : function(response){
45427         
45428         this.response = response;
45429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45430         this.form.afterAction(this, false);
45431     },
45432
45433     processResponse : function(response){
45434         this.response = response;
45435         if(!response.responseText){
45436             return true;
45437         }
45438         this.result = this.handleResponse(response);
45439         return this.result;
45440     },
45441
45442     // utility functions used internally
45443     getUrl : function(appendParams){
45444         var url = this.options.url || this.form.url || this.form.el.dom.action;
45445         if(appendParams){
45446             var p = this.getParams();
45447             if(p){
45448                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45449             }
45450         }
45451         return url;
45452     },
45453
45454     getMethod : function(){
45455         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45456     },
45457
45458     getParams : function(){
45459         var bp = this.form.baseParams;
45460         var p = this.options.params;
45461         if(p){
45462             if(typeof p == "object"){
45463                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45464             }else if(typeof p == 'string' && bp){
45465                 p += '&' + Roo.urlEncode(bp);
45466             }
45467         }else if(bp){
45468             p = Roo.urlEncode(bp);
45469         }
45470         return p;
45471     },
45472
45473     createCallback : function(){
45474         return {
45475             success: this.success,
45476             failure: this.failure,
45477             scope: this,
45478             timeout: (this.form.timeout*1000),
45479             upload: this.form.fileUpload ? this.success : undefined
45480         };
45481     }
45482 };
45483
45484 Roo.form.Action.Submit = function(form, options){
45485     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45486 };
45487
45488 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45489     type : 'submit',
45490
45491     haveProgress : false,
45492     uploadComplete : false,
45493     
45494     // uploadProgress indicator.
45495     uploadProgress : function()
45496     {
45497         if (!this.form.progressUrl) {
45498             return;
45499         }
45500         
45501         if (!this.haveProgress) {
45502             Roo.MessageBox.progress("Uploading", "Uploading");
45503         }
45504         if (this.uploadComplete) {
45505            Roo.MessageBox.hide();
45506            return;
45507         }
45508         
45509         this.haveProgress = true;
45510    
45511         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45512         
45513         var c = new Roo.data.Connection();
45514         c.request({
45515             url : this.form.progressUrl,
45516             params: {
45517                 id : uid
45518             },
45519             method: 'GET',
45520             success : function(req){
45521                //console.log(data);
45522                 var rdata = false;
45523                 var edata;
45524                 try  {
45525                    rdata = Roo.decode(req.responseText)
45526                 } catch (e) {
45527                     Roo.log("Invalid data from server..");
45528                     Roo.log(edata);
45529                     return;
45530                 }
45531                 if (!rdata || !rdata.success) {
45532                     Roo.log(rdata);
45533                     Roo.MessageBox.alert(Roo.encode(rdata));
45534                     return;
45535                 }
45536                 var data = rdata.data;
45537                 
45538                 if (this.uploadComplete) {
45539                    Roo.MessageBox.hide();
45540                    return;
45541                 }
45542                    
45543                 if (data){
45544                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45545                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45546                     );
45547                 }
45548                 this.uploadProgress.defer(2000,this);
45549             },
45550        
45551             failure: function(data) {
45552                 Roo.log('progress url failed ');
45553                 Roo.log(data);
45554             },
45555             scope : this
45556         });
45557            
45558     },
45559     
45560     
45561     run : function()
45562     {
45563         // run get Values on the form, so it syncs any secondary forms.
45564         this.form.getValues();
45565         
45566         var o = this.options;
45567         var method = this.getMethod();
45568         var isPost = method == 'POST';
45569         if(o.clientValidation === false || this.form.isValid()){
45570             
45571             if (this.form.progressUrl) {
45572                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45573                     (new Date() * 1) + '' + Math.random());
45574                     
45575             } 
45576             
45577             
45578             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45579                 form:this.form.el.dom,
45580                 url:this.getUrl(!isPost),
45581                 method: method,
45582                 params:isPost ? this.getParams() : null,
45583                 isUpload: this.form.fileUpload
45584             }));
45585             
45586             this.uploadProgress();
45587
45588         }else if (o.clientValidation !== false){ // client validation failed
45589             this.failureType = Roo.form.Action.CLIENT_INVALID;
45590             this.form.afterAction(this, false);
45591         }
45592     },
45593
45594     success : function(response)
45595     {
45596         this.uploadComplete= true;
45597         if (this.haveProgress) {
45598             Roo.MessageBox.hide();
45599         }
45600         
45601         
45602         var result = this.processResponse(response);
45603         if(result === true || result.success){
45604             this.form.afterAction(this, true);
45605             return;
45606         }
45607         if(result.errors){
45608             this.form.markInvalid(result.errors);
45609             this.failureType = Roo.form.Action.SERVER_INVALID;
45610         }
45611         this.form.afterAction(this, false);
45612     },
45613     failure : function(response)
45614     {
45615         this.uploadComplete= true;
45616         if (this.haveProgress) {
45617             Roo.MessageBox.hide();
45618         }
45619         
45620         this.response = response;
45621         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45622         this.form.afterAction(this, false);
45623     },
45624     
45625     handleResponse : function(response){
45626         if(this.form.errorReader){
45627             var rs = this.form.errorReader.read(response);
45628             var errors = [];
45629             if(rs.records){
45630                 for(var i = 0, len = rs.records.length; i < len; i++) {
45631                     var r = rs.records[i];
45632                     errors[i] = r.data;
45633                 }
45634             }
45635             if(errors.length < 1){
45636                 errors = null;
45637             }
45638             return {
45639                 success : rs.success,
45640                 errors : errors
45641             };
45642         }
45643         var ret = false;
45644         try {
45645             ret = Roo.decode(response.responseText);
45646         } catch (e) {
45647             ret = {
45648                 success: false,
45649                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45650                 errors : []
45651             };
45652         }
45653         return ret;
45654         
45655     }
45656 });
45657
45658
45659 Roo.form.Action.Load = function(form, options){
45660     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45661     this.reader = this.form.reader;
45662 };
45663
45664 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45665     type : 'load',
45666
45667     run : function(){
45668         
45669         Roo.Ajax.request(Roo.apply(
45670                 this.createCallback(), {
45671                     method:this.getMethod(),
45672                     url:this.getUrl(false),
45673                     params:this.getParams()
45674         }));
45675     },
45676
45677     success : function(response){
45678         
45679         var result = this.processResponse(response);
45680         if(result === true || !result.success || !result.data){
45681             this.failureType = Roo.form.Action.LOAD_FAILURE;
45682             this.form.afterAction(this, false);
45683             return;
45684         }
45685         this.form.clearInvalid();
45686         this.form.setValues(result.data);
45687         this.form.afterAction(this, true);
45688     },
45689
45690     handleResponse : function(response){
45691         if(this.form.reader){
45692             var rs = this.form.reader.read(response);
45693             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45694             return {
45695                 success : rs.success,
45696                 data : data
45697             };
45698         }
45699         return Roo.decode(response.responseText);
45700     }
45701 });
45702
45703 Roo.form.Action.ACTION_TYPES = {
45704     'load' : Roo.form.Action.Load,
45705     'submit' : Roo.form.Action.Submit
45706 };/*
45707  * Based on:
45708  * Ext JS Library 1.1.1
45709  * Copyright(c) 2006-2007, Ext JS, LLC.
45710  *
45711  * Originally Released Under LGPL - original licence link has changed is not relivant.
45712  *
45713  * Fork - LGPL
45714  * <script type="text/javascript">
45715  */
45716  
45717 /**
45718  * @class Roo.form.Layout
45719  * @extends Roo.Component
45720  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45721  * @constructor
45722  * @param {Object} config Configuration options
45723  */
45724 Roo.form.Layout = function(config){
45725     var xitems = [];
45726     if (config.items) {
45727         xitems = config.items;
45728         delete config.items;
45729     }
45730     Roo.form.Layout.superclass.constructor.call(this, config);
45731     this.stack = [];
45732     Roo.each(xitems, this.addxtype, this);
45733      
45734 };
45735
45736 Roo.extend(Roo.form.Layout, Roo.Component, {
45737     /**
45738      * @cfg {String/Object} autoCreate
45739      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45740      */
45741     /**
45742      * @cfg {String/Object/Function} style
45743      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45744      * a function which returns such a specification.
45745      */
45746     /**
45747      * @cfg {String} labelAlign
45748      * Valid values are "left," "top" and "right" (defaults to "left")
45749      */
45750     /**
45751      * @cfg {Number} labelWidth
45752      * Fixed width in pixels of all field labels (defaults to undefined)
45753      */
45754     /**
45755      * @cfg {Boolean} clear
45756      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45757      */
45758     clear : true,
45759     /**
45760      * @cfg {String} labelSeparator
45761      * The separator to use after field labels (defaults to ':')
45762      */
45763     labelSeparator : ':',
45764     /**
45765      * @cfg {Boolean} hideLabels
45766      * True to suppress the display of field labels in this layout (defaults to false)
45767      */
45768     hideLabels : false,
45769
45770     // private
45771     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45772     
45773     isLayout : true,
45774     
45775     // private
45776     onRender : function(ct, position){
45777         if(this.el){ // from markup
45778             this.el = Roo.get(this.el);
45779         }else {  // generate
45780             var cfg = this.getAutoCreate();
45781             this.el = ct.createChild(cfg, position);
45782         }
45783         if(this.style){
45784             this.el.applyStyles(this.style);
45785         }
45786         if(this.labelAlign){
45787             this.el.addClass('x-form-label-'+this.labelAlign);
45788         }
45789         if(this.hideLabels){
45790             this.labelStyle = "display:none";
45791             this.elementStyle = "padding-left:0;";
45792         }else{
45793             if(typeof this.labelWidth == 'number'){
45794                 this.labelStyle = "width:"+this.labelWidth+"px;";
45795                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45796             }
45797             if(this.labelAlign == 'top'){
45798                 this.labelStyle = "width:auto;";
45799                 this.elementStyle = "padding-left:0;";
45800             }
45801         }
45802         var stack = this.stack;
45803         var slen = stack.length;
45804         if(slen > 0){
45805             if(!this.fieldTpl){
45806                 var t = new Roo.Template(
45807                     '<div class="x-form-item {5}">',
45808                         '<label for="{0}" style="{2}">{1}{4}</label>',
45809                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45810                         '</div>',
45811                     '</div><div class="x-form-clear-left"></div>'
45812                 );
45813                 t.disableFormats = true;
45814                 t.compile();
45815                 Roo.form.Layout.prototype.fieldTpl = t;
45816             }
45817             for(var i = 0; i < slen; i++) {
45818                 if(stack[i].isFormField){
45819                     this.renderField(stack[i]);
45820                 }else{
45821                     this.renderComponent(stack[i]);
45822                 }
45823             }
45824         }
45825         if(this.clear){
45826             this.el.createChild({cls:'x-form-clear'});
45827         }
45828     },
45829
45830     // private
45831     renderField : function(f){
45832         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45833                f.id, //0
45834                f.fieldLabel, //1
45835                f.labelStyle||this.labelStyle||'', //2
45836                this.elementStyle||'', //3
45837                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45838                f.itemCls||this.itemCls||''  //5
45839        ], true).getPrevSibling());
45840     },
45841
45842     // private
45843     renderComponent : function(c){
45844         c.render(c.isLayout ? this.el : this.el.createChild());    
45845     },
45846     /**
45847      * Adds a object form elements (using the xtype property as the factory method.)
45848      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45849      * @param {Object} config 
45850      */
45851     addxtype : function(o)
45852     {
45853         // create the lement.
45854         o.form = this.form;
45855         var fe = Roo.factory(o, Roo.form);
45856         this.form.allItems.push(fe);
45857         this.stack.push(fe);
45858         
45859         if (fe.isFormField) {
45860             this.form.items.add(fe);
45861         }
45862          
45863         return fe;
45864     }
45865 });
45866
45867 /**
45868  * @class Roo.form.Column
45869  * @extends Roo.form.Layout
45870  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45871  * @constructor
45872  * @param {Object} config Configuration options
45873  */
45874 Roo.form.Column = function(config){
45875     Roo.form.Column.superclass.constructor.call(this, config);
45876 };
45877
45878 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45879     /**
45880      * @cfg {Number/String} width
45881      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45882      */
45883     /**
45884      * @cfg {String/Object} autoCreate
45885      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45886      */
45887
45888     // private
45889     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45890
45891     // private
45892     onRender : function(ct, position){
45893         Roo.form.Column.superclass.onRender.call(this, ct, position);
45894         if(this.width){
45895             this.el.setWidth(this.width);
45896         }
45897     }
45898 });
45899
45900
45901 /**
45902  * @class Roo.form.Row
45903  * @extends Roo.form.Layout
45904  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45905  * @constructor
45906  * @param {Object} config Configuration options
45907  */
45908
45909  
45910 Roo.form.Row = function(config){
45911     Roo.form.Row.superclass.constructor.call(this, config);
45912 };
45913  
45914 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45915       /**
45916      * @cfg {Number/String} width
45917      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45918      */
45919     /**
45920      * @cfg {Number/String} height
45921      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45922      */
45923     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45924     
45925     padWidth : 20,
45926     // private
45927     onRender : function(ct, position){
45928         //console.log('row render');
45929         if(!this.rowTpl){
45930             var t = new Roo.Template(
45931                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45932                     '<label for="{0}" style="{2}">{1}{4}</label>',
45933                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45934                     '</div>',
45935                 '</div>'
45936             );
45937             t.disableFormats = true;
45938             t.compile();
45939             Roo.form.Layout.prototype.rowTpl = t;
45940         }
45941         this.fieldTpl = this.rowTpl;
45942         
45943         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45944         var labelWidth = 100;
45945         
45946         if ((this.labelAlign != 'top')) {
45947             if (typeof this.labelWidth == 'number') {
45948                 labelWidth = this.labelWidth
45949             }
45950             this.padWidth =  20 + labelWidth;
45951             
45952         }
45953         
45954         Roo.form.Column.superclass.onRender.call(this, ct, position);
45955         if(this.width){
45956             this.el.setWidth(this.width);
45957         }
45958         if(this.height){
45959             this.el.setHeight(this.height);
45960         }
45961     },
45962     
45963     // private
45964     renderField : function(f){
45965         f.fieldEl = this.fieldTpl.append(this.el, [
45966                f.id, f.fieldLabel,
45967                f.labelStyle||this.labelStyle||'',
45968                this.elementStyle||'',
45969                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45970                f.itemCls||this.itemCls||'',
45971                f.width ? f.width + this.padWidth : 160 + this.padWidth
45972        ],true);
45973     }
45974 });
45975  
45976
45977 /**
45978  * @class Roo.form.FieldSet
45979  * @extends Roo.form.Layout
45980  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45981  * @constructor
45982  * @param {Object} config Configuration options
45983  */
45984 Roo.form.FieldSet = function(config){
45985     Roo.form.FieldSet.superclass.constructor.call(this, config);
45986 };
45987
45988 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45989     /**
45990      * @cfg {String} legend
45991      * The text to display as the legend for the FieldSet (defaults to '')
45992      */
45993     /**
45994      * @cfg {String/Object} autoCreate
45995      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45996      */
45997
45998     // private
45999     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46000
46001     // private
46002     onRender : function(ct, position){
46003         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46004         if(this.legend){
46005             this.setLegend(this.legend);
46006         }
46007     },
46008
46009     // private
46010     setLegend : function(text){
46011         if(this.rendered){
46012             this.el.child('legend').update(text);
46013         }
46014     }
46015 });/*
46016  * Based on:
46017  * Ext JS Library 1.1.1
46018  * Copyright(c) 2006-2007, Ext JS, LLC.
46019  *
46020  * Originally Released Under LGPL - original licence link has changed is not relivant.
46021  *
46022  * Fork - LGPL
46023  * <script type="text/javascript">
46024  */
46025 /**
46026  * @class Roo.form.VTypes
46027  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46028  * @singleton
46029  */
46030 Roo.form.VTypes = function(){
46031     // closure these in so they are only created once.
46032     var alpha = /^[a-zA-Z_]+$/;
46033     var alphanum = /^[a-zA-Z0-9_]+$/;
46034     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46035     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46036
46037     // All these messages and functions are configurable
46038     return {
46039         /**
46040          * The function used to validate email addresses
46041          * @param {String} value The email address
46042          */
46043         'email' : function(v){
46044             return email.test(v);
46045         },
46046         /**
46047          * The error text to display when the email validation function returns false
46048          * @type String
46049          */
46050         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46051         /**
46052          * The keystroke filter mask to be applied on email input
46053          * @type RegExp
46054          */
46055         'emailMask' : /[a-z0-9_\.\-@]/i,
46056
46057         /**
46058          * The function used to validate URLs
46059          * @param {String} value The URL
46060          */
46061         'url' : function(v){
46062             return url.test(v);
46063         },
46064         /**
46065          * The error text to display when the url validation function returns false
46066          * @type String
46067          */
46068         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46069         
46070         /**
46071          * The function used to validate alpha values
46072          * @param {String} value The value
46073          */
46074         'alpha' : function(v){
46075             return alpha.test(v);
46076         },
46077         /**
46078          * The error text to display when the alpha validation function returns false
46079          * @type String
46080          */
46081         'alphaText' : 'This field should only contain letters and _',
46082         /**
46083          * The keystroke filter mask to be applied on alpha input
46084          * @type RegExp
46085          */
46086         'alphaMask' : /[a-z_]/i,
46087
46088         /**
46089          * The function used to validate alphanumeric values
46090          * @param {String} value The value
46091          */
46092         'alphanum' : function(v){
46093             return alphanum.test(v);
46094         },
46095         /**
46096          * The error text to display when the alphanumeric validation function returns false
46097          * @type String
46098          */
46099         'alphanumText' : 'This field should only contain letters, numbers and _',
46100         /**
46101          * The keystroke filter mask to be applied on alphanumeric input
46102          * @type RegExp
46103          */
46104         'alphanumMask' : /[a-z0-9_]/i
46105     };
46106 }();//<script type="text/javascript">
46107
46108 /**
46109  * @class Roo.form.FCKeditor
46110  * @extends Roo.form.TextArea
46111  * Wrapper around the FCKEditor http://www.fckeditor.net
46112  * @constructor
46113  * Creates a new FCKeditor
46114  * @param {Object} config Configuration options
46115  */
46116 Roo.form.FCKeditor = function(config){
46117     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46118     this.addEvents({
46119          /**
46120          * @event editorinit
46121          * Fired when the editor is initialized - you can add extra handlers here..
46122          * @param {FCKeditor} this
46123          * @param {Object} the FCK object.
46124          */
46125         editorinit : true
46126     });
46127     
46128     
46129 };
46130 Roo.form.FCKeditor.editors = { };
46131 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46132 {
46133     //defaultAutoCreate : {
46134     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46135     //},
46136     // private
46137     /**
46138      * @cfg {Object} fck options - see fck manual for details.
46139      */
46140     fckconfig : false,
46141     
46142     /**
46143      * @cfg {Object} fck toolbar set (Basic or Default)
46144      */
46145     toolbarSet : 'Basic',
46146     /**
46147      * @cfg {Object} fck BasePath
46148      */ 
46149     basePath : '/fckeditor/',
46150     
46151     
46152     frame : false,
46153     
46154     value : '',
46155     
46156    
46157     onRender : function(ct, position)
46158     {
46159         if(!this.el){
46160             this.defaultAutoCreate = {
46161                 tag: "textarea",
46162                 style:"width:300px;height:60px;",
46163                 autocomplete: "off"
46164             };
46165         }
46166         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46167         /*
46168         if(this.grow){
46169             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46170             if(this.preventScrollbars){
46171                 this.el.setStyle("overflow", "hidden");
46172             }
46173             this.el.setHeight(this.growMin);
46174         }
46175         */
46176         //console.log('onrender' + this.getId() );
46177         Roo.form.FCKeditor.editors[this.getId()] = this;
46178          
46179
46180         this.replaceTextarea() ;
46181         
46182     },
46183     
46184     getEditor : function() {
46185         return this.fckEditor;
46186     },
46187     /**
46188      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46189      * @param {Mixed} value The value to set
46190      */
46191     
46192     
46193     setValue : function(value)
46194     {
46195         //console.log('setValue: ' + value);
46196         
46197         if(typeof(value) == 'undefined') { // not sure why this is happending...
46198             return;
46199         }
46200         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46201         
46202         //if(!this.el || !this.getEditor()) {
46203         //    this.value = value;
46204             //this.setValue.defer(100,this,[value]);    
46205         //    return;
46206         //} 
46207         
46208         if(!this.getEditor()) {
46209             return;
46210         }
46211         
46212         this.getEditor().SetData(value);
46213         
46214         //
46215
46216     },
46217
46218     /**
46219      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46220      * @return {Mixed} value The field value
46221      */
46222     getValue : function()
46223     {
46224         
46225         if (this.frame && this.frame.dom.style.display == 'none') {
46226             return Roo.form.FCKeditor.superclass.getValue.call(this);
46227         }
46228         
46229         if(!this.el || !this.getEditor()) {
46230            
46231            // this.getValue.defer(100,this); 
46232             return this.value;
46233         }
46234        
46235         
46236         var value=this.getEditor().GetData();
46237         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46238         return Roo.form.FCKeditor.superclass.getValue.call(this);
46239         
46240
46241     },
46242
46243     /**
46244      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46245      * @return {Mixed} value The field value
46246      */
46247     getRawValue : function()
46248     {
46249         if (this.frame && this.frame.dom.style.display == 'none') {
46250             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46251         }
46252         
46253         if(!this.el || !this.getEditor()) {
46254             //this.getRawValue.defer(100,this); 
46255             return this.value;
46256             return;
46257         }
46258         
46259         
46260         
46261         var value=this.getEditor().GetData();
46262         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46263         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46264          
46265     },
46266     
46267     setSize : function(w,h) {
46268         
46269         
46270         
46271         //if (this.frame && this.frame.dom.style.display == 'none') {
46272         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46273         //    return;
46274         //}
46275         //if(!this.el || !this.getEditor()) {
46276         //    this.setSize.defer(100,this, [w,h]); 
46277         //    return;
46278         //}
46279         
46280         
46281         
46282         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46283         
46284         this.frame.dom.setAttribute('width', w);
46285         this.frame.dom.setAttribute('height', h);
46286         this.frame.setSize(w,h);
46287         
46288     },
46289     
46290     toggleSourceEdit : function(value) {
46291         
46292       
46293          
46294         this.el.dom.style.display = value ? '' : 'none';
46295         this.frame.dom.style.display = value ?  'none' : '';
46296         
46297     },
46298     
46299     
46300     focus: function(tag)
46301     {
46302         if (this.frame.dom.style.display == 'none') {
46303             return Roo.form.FCKeditor.superclass.focus.call(this);
46304         }
46305         if(!this.el || !this.getEditor()) {
46306             this.focus.defer(100,this, [tag]); 
46307             return;
46308         }
46309         
46310         
46311         
46312         
46313         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46314         this.getEditor().Focus();
46315         if (tgs.length) {
46316             if (!this.getEditor().Selection.GetSelection()) {
46317                 this.focus.defer(100,this, [tag]); 
46318                 return;
46319             }
46320             
46321             
46322             var r = this.getEditor().EditorDocument.createRange();
46323             r.setStart(tgs[0],0);
46324             r.setEnd(tgs[0],0);
46325             this.getEditor().Selection.GetSelection().removeAllRanges();
46326             this.getEditor().Selection.GetSelection().addRange(r);
46327             this.getEditor().Focus();
46328         }
46329         
46330     },
46331     
46332     
46333     
46334     replaceTextarea : function()
46335     {
46336         if ( document.getElementById( this.getId() + '___Frame' ) )
46337             return ;
46338         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46339         //{
46340             // We must check the elements firstly using the Id and then the name.
46341         var oTextarea = document.getElementById( this.getId() );
46342         
46343         var colElementsByName = document.getElementsByName( this.getId() ) ;
46344          
46345         oTextarea.style.display = 'none' ;
46346
46347         if ( oTextarea.tabIndex ) {            
46348             this.TabIndex = oTextarea.tabIndex ;
46349         }
46350         
46351         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46352         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46353         this.frame = Roo.get(this.getId() + '___Frame')
46354     },
46355     
46356     _getConfigHtml : function()
46357     {
46358         var sConfig = '' ;
46359
46360         for ( var o in this.fckconfig ) {
46361             sConfig += sConfig.length > 0  ? '&amp;' : '';
46362             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46363         }
46364
46365         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46366     },
46367     
46368     
46369     _getIFrameHtml : function()
46370     {
46371         var sFile = 'fckeditor.html' ;
46372         /* no idea what this is about..
46373         try
46374         {
46375             if ( (/fcksource=true/i).test( window.top.location.search ) )
46376                 sFile = 'fckeditor.original.html' ;
46377         }
46378         catch (e) { 
46379         */
46380
46381         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46382         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46383         
46384         
46385         var html = '<iframe id="' + this.getId() +
46386             '___Frame" src="' + sLink +
46387             '" width="' + this.width +
46388             '" height="' + this.height + '"' +
46389             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46390             ' frameborder="0" scrolling="no"></iframe>' ;
46391
46392         return html ;
46393     },
46394     
46395     _insertHtmlBefore : function( html, element )
46396     {
46397         if ( element.insertAdjacentHTML )       {
46398             // IE
46399             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46400         } else { // Gecko
46401             var oRange = document.createRange() ;
46402             oRange.setStartBefore( element ) ;
46403             var oFragment = oRange.createContextualFragment( html );
46404             element.parentNode.insertBefore( oFragment, element ) ;
46405         }
46406     }
46407     
46408     
46409   
46410     
46411     
46412     
46413     
46414
46415 });
46416
46417 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46418
46419 function FCKeditor_OnComplete(editorInstance){
46420     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46421     f.fckEditor = editorInstance;
46422     //console.log("loaded");
46423     f.fireEvent('editorinit', f, editorInstance);
46424
46425   
46426
46427  
46428
46429
46430
46431
46432
46433
46434
46435
46436
46437
46438
46439
46440
46441
46442
46443 //<script type="text/javascript">
46444 /**
46445  * @class Roo.form.GridField
46446  * @extends Roo.form.Field
46447  * Embed a grid (or editable grid into a form)
46448  * STATUS ALPHA
46449  * 
46450  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46451  * it needs 
46452  * xgrid.store = Roo.data.Store
46453  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46454  * xgrid.store.reader = Roo.data.JsonReader 
46455  * 
46456  * 
46457  * @constructor
46458  * Creates a new GridField
46459  * @param {Object} config Configuration options
46460  */
46461 Roo.form.GridField = function(config){
46462     Roo.form.GridField.superclass.constructor.call(this, config);
46463      
46464 };
46465
46466 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46467     /**
46468      * @cfg {Number} width  - used to restrict width of grid..
46469      */
46470     width : 100,
46471     /**
46472      * @cfg {Number} height - used to restrict height of grid..
46473      */
46474     height : 50,
46475      /**
46476      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46477          * 
46478          *}
46479      */
46480     xgrid : false, 
46481     /**
46482      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46483      * {tag: "input", type: "checkbox", autocomplete: "off"})
46484      */
46485    // defaultAutoCreate : { tag: 'div' },
46486     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46487     /**
46488      * @cfg {String} addTitle Text to include for adding a title.
46489      */
46490     addTitle : false,
46491     //
46492     onResize : function(){
46493         Roo.form.Field.superclass.onResize.apply(this, arguments);
46494     },
46495
46496     initEvents : function(){
46497         // Roo.form.Checkbox.superclass.initEvents.call(this);
46498         // has no events...
46499        
46500     },
46501
46502
46503     getResizeEl : function(){
46504         return this.wrap;
46505     },
46506
46507     getPositionEl : function(){
46508         return this.wrap;
46509     },
46510
46511     // private
46512     onRender : function(ct, position){
46513         
46514         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46515         var style = this.style;
46516         delete this.style;
46517         
46518         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46519         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46520         this.viewEl = this.wrap.createChild({ tag: 'div' });
46521         if (style) {
46522             this.viewEl.applyStyles(style);
46523         }
46524         if (this.width) {
46525             this.viewEl.setWidth(this.width);
46526         }
46527         if (this.height) {
46528             this.viewEl.setHeight(this.height);
46529         }
46530         //if(this.inputValue !== undefined){
46531         //this.setValue(this.value);
46532         
46533         
46534         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46535         
46536         
46537         this.grid.render();
46538         this.grid.getDataSource().on('remove', this.refreshValue, this);
46539         this.grid.getDataSource().on('update', this.refreshValue, this);
46540         this.grid.on('afteredit', this.refreshValue, this);
46541  
46542     },
46543      
46544     
46545     /**
46546      * Sets the value of the item. 
46547      * @param {String} either an object  or a string..
46548      */
46549     setValue : function(v){
46550         //this.value = v;
46551         v = v || []; // empty set..
46552         // this does not seem smart - it really only affects memoryproxy grids..
46553         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46554             var ds = this.grid.getDataSource();
46555             // assumes a json reader..
46556             var data = {}
46557             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46558             ds.loadData( data);
46559         }
46560         // clear selection so it does not get stale.
46561         if (this.grid.sm) { 
46562             this.grid.sm.clearSelections();
46563         }
46564         
46565         Roo.form.GridField.superclass.setValue.call(this, v);
46566         this.refreshValue();
46567         // should load data in the grid really....
46568     },
46569     
46570     // private
46571     refreshValue: function() {
46572          var val = [];
46573         this.grid.getDataSource().each(function(r) {
46574             val.push(r.data);
46575         });
46576         this.el.dom.value = Roo.encode(val);
46577     }
46578     
46579      
46580     
46581     
46582 });/*
46583  * Based on:
46584  * Ext JS Library 1.1.1
46585  * Copyright(c) 2006-2007, Ext JS, LLC.
46586  *
46587  * Originally Released Under LGPL - original licence link has changed is not relivant.
46588  *
46589  * Fork - LGPL
46590  * <script type="text/javascript">
46591  */
46592 /**
46593  * @class Roo.form.DisplayField
46594  * @extends Roo.form.Field
46595  * A generic Field to display non-editable data.
46596  * @constructor
46597  * Creates a new Display Field item.
46598  * @param {Object} config Configuration options
46599  */
46600 Roo.form.DisplayField = function(config){
46601     Roo.form.DisplayField.superclass.constructor.call(this, config);
46602     
46603 };
46604
46605 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46606     inputType:      'hidden',
46607     allowBlank:     true,
46608     readOnly:         true,
46609     
46610  
46611     /**
46612      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46613      */
46614     focusClass : undefined,
46615     /**
46616      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46617      */
46618     fieldClass: 'x-form-field',
46619     
46620      /**
46621      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46622      */
46623     valueRenderer: undefined,
46624     
46625     width: 100,
46626     /**
46627      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46628      * {tag: "input", type: "checkbox", autocomplete: "off"})
46629      */
46630      
46631  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46632
46633     onResize : function(){
46634         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46635         
46636     },
46637
46638     initEvents : function(){
46639         // Roo.form.Checkbox.superclass.initEvents.call(this);
46640         // has no events...
46641        
46642     },
46643
46644
46645     getResizeEl : function(){
46646         return this.wrap;
46647     },
46648
46649     getPositionEl : function(){
46650         return this.wrap;
46651     },
46652
46653     // private
46654     onRender : function(ct, position){
46655         
46656         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46657         //if(this.inputValue !== undefined){
46658         this.wrap = this.el.wrap();
46659         
46660         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46661         
46662         if (this.bodyStyle) {
46663             this.viewEl.applyStyles(this.bodyStyle);
46664         }
46665         //this.viewEl.setStyle('padding', '2px');
46666         
46667         this.setValue(this.value);
46668         
46669     },
46670 /*
46671     // private
46672     initValue : Roo.emptyFn,
46673
46674   */
46675
46676         // private
46677     onClick : function(){
46678         
46679     },
46680
46681     /**
46682      * Sets the checked state of the checkbox.
46683      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46684      */
46685     setValue : function(v){
46686         this.value = v;
46687         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46688         // this might be called before we have a dom element..
46689         if (!this.viewEl) {
46690             return;
46691         }
46692         this.viewEl.dom.innerHTML = html;
46693         Roo.form.DisplayField.superclass.setValue.call(this, v);
46694
46695     }
46696 });/*
46697  * 
46698  * Licence- LGPL
46699  * 
46700  */
46701
46702 /**
46703  * @class Roo.form.DayPicker
46704  * @extends Roo.form.Field
46705  * A Day picker show [M] [T] [W] ....
46706  * @constructor
46707  * Creates a new Day Picker
46708  * @param {Object} config Configuration options
46709  */
46710 Roo.form.DayPicker= function(config){
46711     Roo.form.DayPicker.superclass.constructor.call(this, config);
46712      
46713 };
46714
46715 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46716     /**
46717      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46718      */
46719     focusClass : undefined,
46720     /**
46721      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46722      */
46723     fieldClass: "x-form-field",
46724    
46725     /**
46726      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46727      * {tag: "input", type: "checkbox", autocomplete: "off"})
46728      */
46729     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46730     
46731    
46732     actionMode : 'viewEl', 
46733     //
46734     // private
46735  
46736     inputType : 'hidden',
46737     
46738      
46739     inputElement: false, // real input element?
46740     basedOn: false, // ????
46741     
46742     isFormField: true, // not sure where this is needed!!!!
46743
46744     onResize : function(){
46745         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46746         if(!this.boxLabel){
46747             this.el.alignTo(this.wrap, 'c-c');
46748         }
46749     },
46750
46751     initEvents : function(){
46752         Roo.form.Checkbox.superclass.initEvents.call(this);
46753         this.el.on("click", this.onClick,  this);
46754         this.el.on("change", this.onClick,  this);
46755     },
46756
46757
46758     getResizeEl : function(){
46759         return this.wrap;
46760     },
46761
46762     getPositionEl : function(){
46763         return this.wrap;
46764     },
46765
46766     
46767     // private
46768     onRender : function(ct, position){
46769         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46770        
46771         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46772         
46773         var r1 = '<table><tr>';
46774         var r2 = '<tr class="x-form-daypick-icons">';
46775         for (var i=0; i < 7; i++) {
46776             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46777             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46778         }
46779         
46780         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46781         viewEl.select('img').on('click', this.onClick, this);
46782         this.viewEl = viewEl;   
46783         
46784         
46785         // this will not work on Chrome!!!
46786         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46787         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46788         
46789         
46790           
46791
46792     },
46793
46794     // private
46795     initValue : Roo.emptyFn,
46796
46797     /**
46798      * Returns the checked state of the checkbox.
46799      * @return {Boolean} True if checked, else false
46800      */
46801     getValue : function(){
46802         return this.el.dom.value;
46803         
46804     },
46805
46806         // private
46807     onClick : function(e){ 
46808         //this.setChecked(!this.checked);
46809         Roo.get(e.target).toggleClass('x-menu-item-checked');
46810         this.refreshValue();
46811         //if(this.el.dom.checked != this.checked){
46812         //    this.setValue(this.el.dom.checked);
46813        // }
46814     },
46815     
46816     // private
46817     refreshValue : function()
46818     {
46819         var val = '';
46820         this.viewEl.select('img',true).each(function(e,i,n)  {
46821             val += e.is(".x-menu-item-checked") ? String(n) : '';
46822         });
46823         this.setValue(val, true);
46824     },
46825
46826     /**
46827      * Sets the checked state of the checkbox.
46828      * On is always based on a string comparison between inputValue and the param.
46829      * @param {Boolean/String} value - the value to set 
46830      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46831      */
46832     setValue : function(v,suppressEvent){
46833         if (!this.el.dom) {
46834             return;
46835         }
46836         var old = this.el.dom.value ;
46837         this.el.dom.value = v;
46838         if (suppressEvent) {
46839             return ;
46840         }
46841          
46842         // update display..
46843         this.viewEl.select('img',true).each(function(e,i,n)  {
46844             
46845             var on = e.is(".x-menu-item-checked");
46846             var newv = v.indexOf(String(n)) > -1;
46847             if (on != newv) {
46848                 e.toggleClass('x-menu-item-checked');
46849             }
46850             
46851         });
46852         
46853         
46854         this.fireEvent('change', this, v, old);
46855         
46856         
46857     },
46858    
46859     // handle setting of hidden value by some other method!!?!?
46860     setFromHidden: function()
46861     {
46862         if(!this.el){
46863             return;
46864         }
46865         //console.log("SET FROM HIDDEN");
46866         //alert('setFrom hidden');
46867         this.setValue(this.el.dom.value);
46868     },
46869     
46870     onDestroy : function()
46871     {
46872         if(this.viewEl){
46873             Roo.get(this.viewEl).remove();
46874         }
46875          
46876         Roo.form.DayPicker.superclass.onDestroy.call(this);
46877     }
46878
46879 });/*
46880  * RooJS Library 1.1.1
46881  * Copyright(c) 2008-2011  Alan Knowles
46882  *
46883  * License - LGPL
46884  */
46885  
46886
46887 /**
46888  * @class Roo.form.ComboCheck
46889  * @extends Roo.form.ComboBox
46890  * A combobox for multiple select items.
46891  *
46892  * FIXME - could do with a reset button..
46893  * 
46894  * @constructor
46895  * Create a new ComboCheck
46896  * @param {Object} config Configuration options
46897  */
46898 Roo.form.ComboCheck = function(config){
46899     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46900     // should verify some data...
46901     // like
46902     // hiddenName = required..
46903     // displayField = required
46904     // valudField == required
46905     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46906     var _t = this;
46907     Roo.each(req, function(e) {
46908         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46909             throw "Roo.form.ComboCheck : missing value for: " + e;
46910         }
46911     });
46912     
46913     
46914 };
46915
46916 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46917      
46918      
46919     editable : false,
46920      
46921     selectedClass: 'x-menu-item-checked', 
46922     
46923     // private
46924     onRender : function(ct, position){
46925         var _t = this;
46926         
46927         
46928         
46929         if(!this.tpl){
46930             var cls = 'x-combo-list';
46931
46932             
46933             this.tpl =  new Roo.Template({
46934                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46935                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46936                    '<span>{' + this.displayField + '}</span>' +
46937                     '</div>' 
46938                 
46939             });
46940         }
46941  
46942         
46943         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46944         this.view.singleSelect = false;
46945         this.view.multiSelect = true;
46946         this.view.toggleSelect = true;
46947         this.pageTb.add(new Roo.Toolbar.Fill(), {
46948             
46949             text: 'Done',
46950             handler: function()
46951             {
46952                 _t.collapse();
46953             }
46954         });
46955     },
46956     
46957     onViewOver : function(e, t){
46958         // do nothing...
46959         return;
46960         
46961     },
46962     
46963     onViewClick : function(doFocus,index){
46964         return;
46965         
46966     },
46967     select: function () {
46968         //Roo.log("SELECT CALLED");
46969     },
46970      
46971     selectByValue : function(xv, scrollIntoView){
46972         var ar = this.getValueArray();
46973         var sels = [];
46974         
46975         Roo.each(ar, function(v) {
46976             if(v === undefined || v === null){
46977                 return;
46978             }
46979             var r = this.findRecord(this.valueField, v);
46980             if(r){
46981                 sels.push(this.store.indexOf(r))
46982                 
46983             }
46984         },this);
46985         this.view.select(sels);
46986         return false;
46987     },
46988     
46989     
46990     
46991     onSelect : function(record, index){
46992        // Roo.log("onselect Called");
46993        // this is only called by the clear button now..
46994         this.view.clearSelections();
46995         this.setValue('[]');
46996         if (this.value != this.valueBefore) {
46997             this.fireEvent('change', this, this.value, this.valueBefore);
46998             this.valueBefore = this.value;
46999         }
47000     },
47001     getValueArray : function()
47002     {
47003         var ar = [] ;
47004         
47005         try {
47006             //Roo.log(this.value);
47007             if (typeof(this.value) == 'undefined') {
47008                 return [];
47009             }
47010             var ar = Roo.decode(this.value);
47011             return  ar instanceof Array ? ar : []; //?? valid?
47012             
47013         } catch(e) {
47014             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47015             return [];
47016         }
47017          
47018     },
47019     expand : function ()
47020     {
47021         
47022         Roo.form.ComboCheck.superclass.expand.call(this);
47023         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47024         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47025         
47026
47027     },
47028     
47029     collapse : function(){
47030         Roo.form.ComboCheck.superclass.collapse.call(this);
47031         var sl = this.view.getSelectedIndexes();
47032         var st = this.store;
47033         var nv = [];
47034         var tv = [];
47035         var r;
47036         Roo.each(sl, function(i) {
47037             r = st.getAt(i);
47038             nv.push(r.get(this.valueField));
47039         },this);
47040         this.setValue(Roo.encode(nv));
47041         if (this.value != this.valueBefore) {
47042
47043             this.fireEvent('change', this, this.value, this.valueBefore);
47044             this.valueBefore = this.value;
47045         }
47046         
47047     },
47048     
47049     setValue : function(v){
47050         // Roo.log(v);
47051         this.value = v;
47052         
47053         var vals = this.getValueArray();
47054         var tv = [];
47055         Roo.each(vals, function(k) {
47056             var r = this.findRecord(this.valueField, k);
47057             if(r){
47058                 tv.push(r.data[this.displayField]);
47059             }else if(this.valueNotFoundText !== undefined){
47060                 tv.push( this.valueNotFoundText );
47061             }
47062         },this);
47063        // Roo.log(tv);
47064         
47065         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47066         this.hiddenField.value = v;
47067         this.value = v;
47068     }
47069     
47070 });/*
47071  * Based on:
47072  * Ext JS Library 1.1.1
47073  * Copyright(c) 2006-2007, Ext JS, LLC.
47074  *
47075  * Originally Released Under LGPL - original licence link has changed is not relivant.
47076  *
47077  * Fork - LGPL
47078  * <script type="text/javascript">
47079  */
47080  
47081 /**
47082  * @class Roo.form.Signature
47083  * @extends Roo.form.Field
47084  * Signature field.  
47085  * @constructor
47086  * 
47087  * @param {Object} config Configuration options
47088  */
47089
47090 Roo.form.Signature = function(config){
47091     Roo.form.Signature.superclass.constructor.call(this, config);
47092     
47093     this.addEvents({// not in used??
47094          /**
47095          * @event confirm
47096          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47097              * @param {Roo.form.Signature} combo This combo box
47098              */
47099         'confirm' : true,
47100         /**
47101          * @event reset
47102          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47103              * @param {Roo.form.ComboBox} combo This combo box
47104              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47105              */
47106         'reset' : true
47107     });
47108 };
47109
47110 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47111     /**
47112      * @cfg {Object} labels Label to use when rendering a form.
47113      * defaults to 
47114      * labels : { 
47115      *      clear : "Clear",
47116      *      confirm : "Confirm"
47117      *  }
47118      */
47119     labels : { 
47120         clear : "Clear",
47121         confirm : "Confirm"
47122     },
47123     /**
47124      * @cfg {Number} width The signature panel width (defaults to 300)
47125      */
47126     width: 300,
47127     /**
47128      * @cfg {Number} height The signature panel height (defaults to 100)
47129      */
47130     height : 100,
47131     /**
47132      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47133      */
47134     allowBlank : false,
47135     
47136     //private
47137     // {Object} signPanel The signature SVG panel element (defaults to {})
47138     signPanel : {},
47139     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47140     isMouseDown : false,
47141     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47142     isConfirmed : false,
47143     // {String} signatureTmp SVG mapping string (defaults to empty string)
47144     signatureTmp : '',
47145     
47146     
47147     defaultAutoCreate : { // modified by initCompnoent..
47148         tag: "input",
47149         type:"hidden"
47150     },
47151
47152     // private
47153     onRender : function(ct, position){
47154         
47155         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47156         
47157         this.wrap = this.el.wrap({
47158             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47159         });
47160         
47161         this.createToolbar(this);
47162         this.signPanel = this.wrap.createChild({
47163                 tag: 'div',
47164                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47165             }, this.el
47166         );
47167             
47168         this.svgID = Roo.id();
47169         this.svgEl = this.signPanel.createChild({
47170               xmlns : 'http://www.w3.org/2000/svg',
47171               tag : 'svg',
47172               id : this.svgID + "-svg",
47173               width: this.width,
47174               height: this.height,
47175               viewBox: '0 0 '+this.width+' '+this.height,
47176               cn : [
47177                 {
47178                     tag: "rect",
47179                     id: this.svgID + "-svg-r",
47180                     width: this.width,
47181                     height: this.height,
47182                     fill: "#ffa"
47183                 },
47184                 {
47185                     tag: "line",
47186                     id: this.svgID + "-svg-l",
47187                     x1: "0", // start
47188                     y1: (this.height*0.8), // start set the line in 80% of height
47189                     x2: this.width, // end
47190                     y2: (this.height*0.8), // end set the line in 80% of height
47191                     'stroke': "#666",
47192                     'stroke-width': "1",
47193                     'stroke-dasharray': "3",
47194                     'shape-rendering': "crispEdges",
47195                     'pointer-events': "none"
47196                 },
47197                 {
47198                     tag: "path",
47199                     id: this.svgID + "-svg-p",
47200                     'stroke': "navy",
47201                     'stroke-width': "3",
47202                     'fill': "none",
47203                     'pointer-events': 'none'
47204                 }
47205               ]
47206         });
47207         this.createSVG();
47208         this.svgBox = this.svgEl.dom.getScreenCTM();
47209     },
47210     createSVG : function(){ 
47211         var svg = this.signPanel;
47212         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47213         var t = this;
47214
47215         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47216         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47217         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47218         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47219         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47220         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47221         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47222         
47223     },
47224     isTouchEvent : function(e){
47225         return e.type.match(/^touch/);
47226     },
47227     getCoords : function (e) {
47228         var pt    = this.svgEl.dom.createSVGPoint();
47229         pt.x = e.clientX; 
47230         pt.y = e.clientY;
47231         if (this.isTouchEvent(e)) {
47232             pt.x =  e.targetTouches[0].clientX 
47233             pt.y = e.targetTouches[0].clientY;
47234         }
47235         var a = this.svgEl.dom.getScreenCTM();
47236         var b = a.inverse();
47237         var mx = pt.matrixTransform(b);
47238         return mx.x + ',' + mx.y;
47239     },
47240     //mouse event headler 
47241     down : function (e) {
47242         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47243         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47244         
47245         this.isMouseDown = true;
47246         
47247         e.preventDefault();
47248     },
47249     move : function (e) {
47250         if (this.isMouseDown) {
47251             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47252             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47253         }
47254         
47255         e.preventDefault();
47256     },
47257     up : function (e) {
47258         this.isMouseDown = false;
47259         var sp = this.signatureTmp.split(' ');
47260         
47261         if(sp.length > 1){
47262             if(!sp[sp.length-2].match(/^L/)){
47263                 sp.pop();
47264                 sp.pop();
47265                 sp.push("");
47266                 this.signatureTmp = sp.join(" ");
47267             }
47268         }
47269         if(this.getValue() != this.signatureTmp){
47270             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47271             this.isConfirmed = false;
47272         }
47273         e.preventDefault();
47274     },
47275     
47276     /**
47277      * Protected method that will not generally be called directly. It
47278      * is called when the editor creates its toolbar. Override this method if you need to
47279      * add custom toolbar buttons.
47280      * @param {HtmlEditor} editor
47281      */
47282     createToolbar : function(editor){
47283          function btn(id, toggle, handler){
47284             var xid = fid + '-'+ id ;
47285             return {
47286                 id : xid,
47287                 cmd : id,
47288                 cls : 'x-btn-icon x-edit-'+id,
47289                 enableToggle:toggle !== false,
47290                 scope: editor, // was editor...
47291                 handler:handler||editor.relayBtnCmd,
47292                 clickEvent:'mousedown',
47293                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47294                 tabIndex:-1
47295             };
47296         }
47297         
47298         
47299         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47300         this.tb = tb;
47301         this.tb.add(
47302            {
47303                 cls : ' x-signature-btn x-signature-'+id,
47304                 scope: editor, // was editor...
47305                 handler: this.reset,
47306                 clickEvent:'mousedown',
47307                 text: this.labels.clear
47308             },
47309             {
47310                  xtype : 'Fill',
47311                  xns: Roo.Toolbar
47312             }, 
47313             {
47314                 cls : '  x-signature-btn x-signature-'+id,
47315                 scope: editor, // was editor...
47316                 handler: this.confirmHandler,
47317                 clickEvent:'mousedown',
47318                 text: this.labels.confirm
47319             }
47320         );
47321     
47322     },
47323     //public
47324     /**
47325      * when user is clicked confirm then show this image.....
47326      * 
47327      * @return {String} Image Data URI
47328      */
47329     getImageDataURI : function(){
47330         var svg = this.svgEl.dom.parentNode.innerHTML;
47331         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47332         return src; 
47333     },
47334     /**
47335      * 
47336      * @return {Boolean} this.isConfirmed
47337      */
47338     getConfirmed : function(){
47339         return this.isConfirmed;
47340     },
47341     /**
47342      * 
47343      * @return {Number} this.width
47344      */
47345     getWidth : function(){
47346         return this.width;
47347     },
47348     /**
47349      * 
47350      * @return {Number} this.height
47351      */
47352     getHeight : function(){
47353         return this.height;
47354     },
47355     // private
47356     getSignature : function(){
47357         return this.signatureTmp;
47358     },
47359     // private
47360     reset : function(){
47361         this.signatureTmp = '';
47362         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47363         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47364         this.isConfirmed = false;
47365         Roo.form.Signature.superclass.reset.call(this);
47366     },
47367     setSignature : function(s){
47368         this.signatureTmp = s;
47369         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47370         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47371         this.setValue(s);
47372         this.isConfirmed = false;
47373         Roo.form.Signature.superclass.reset.call(this);
47374     }, 
47375     test : function(){
47376 //        Roo.log(this.signPanel.dom.contentWindow.up())
47377     },
47378     //private
47379     setConfirmed : function(){
47380         
47381         
47382         
47383 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47384     },
47385     // private
47386     confirmHandler : function(){
47387         if(!this.getSignature()){
47388             return;
47389         }
47390         
47391         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47392         this.setValue(this.getSignature());
47393         this.isConfirmed = true;
47394         
47395         this.fireEvent('confirm', this);
47396     },
47397     // private
47398     // Subclasses should provide the validation implementation by overriding this
47399     validateValue : function(value){
47400         if(this.allowBlank){
47401             return true;
47402         }
47403         
47404         if(this.isConfirmed){
47405             return true;
47406         }
47407         return false;
47408     }
47409 });/*
47410  * Based on:
47411  * Ext JS Library 1.1.1
47412  * Copyright(c) 2006-2007, Ext JS, LLC.
47413  *
47414  * Originally Released Under LGPL - original licence link has changed is not relivant.
47415  *
47416  * Fork - LGPL
47417  * <script type="text/javascript">
47418  */
47419  
47420
47421 /**
47422  * @class Roo.form.ComboBox
47423  * @extends Roo.form.TriggerField
47424  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47425  * @constructor
47426  * Create a new ComboBox.
47427  * @param {Object} config Configuration options
47428  */
47429 Roo.form.Select = function(config){
47430     Roo.form.Select.superclass.constructor.call(this, config);
47431      
47432 };
47433
47434 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47435     /**
47436      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47437      */
47438     /**
47439      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47440      * rendering into an Roo.Editor, defaults to false)
47441      */
47442     /**
47443      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47444      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47445      */
47446     /**
47447      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47448      */
47449     /**
47450      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47451      * the dropdown list (defaults to undefined, with no header element)
47452      */
47453
47454      /**
47455      * @cfg {String/Roo.Template} tpl The template to use to render the output
47456      */
47457      
47458     // private
47459     defaultAutoCreate : {tag: "select"  },
47460     /**
47461      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47462      */
47463     listWidth: undefined,
47464     /**
47465      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47466      * mode = 'remote' or 'text' if mode = 'local')
47467      */
47468     displayField: undefined,
47469     /**
47470      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47471      * mode = 'remote' or 'value' if mode = 'local'). 
47472      * Note: use of a valueField requires the user make a selection
47473      * in order for a value to be mapped.
47474      */
47475     valueField: undefined,
47476     
47477     
47478     /**
47479      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47480      * field's data value (defaults to the underlying DOM element's name)
47481      */
47482     hiddenName: undefined,
47483     /**
47484      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47485      */
47486     listClass: '',
47487     /**
47488      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47489      */
47490     selectedClass: 'x-combo-selected',
47491     /**
47492      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47493      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47494      * which displays a downward arrow icon).
47495      */
47496     triggerClass : 'x-form-arrow-trigger',
47497     /**
47498      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47499      */
47500     shadow:'sides',
47501     /**
47502      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47503      * anchor positions (defaults to 'tl-bl')
47504      */
47505     listAlign: 'tl-bl?',
47506     /**
47507      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47508      */
47509     maxHeight: 300,
47510     /**
47511      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47512      * query specified by the allQuery config option (defaults to 'query')
47513      */
47514     triggerAction: 'query',
47515     /**
47516      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47517      * (defaults to 4, does not apply if editable = false)
47518      */
47519     minChars : 4,
47520     /**
47521      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47522      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47523      */
47524     typeAhead: false,
47525     /**
47526      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47527      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47528      */
47529     queryDelay: 500,
47530     /**
47531      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47532      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47533      */
47534     pageSize: 0,
47535     /**
47536      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47537      * when editable = true (defaults to false)
47538      */
47539     selectOnFocus:false,
47540     /**
47541      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47542      */
47543     queryParam: 'query',
47544     /**
47545      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47546      * when mode = 'remote' (defaults to 'Loading...')
47547      */
47548     loadingText: 'Loading...',
47549     /**
47550      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47551      */
47552     resizable: false,
47553     /**
47554      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47555      */
47556     handleHeight : 8,
47557     /**
47558      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47559      * traditional select (defaults to true)
47560      */
47561     editable: true,
47562     /**
47563      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47564      */
47565     allQuery: '',
47566     /**
47567      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47568      */
47569     mode: 'remote',
47570     /**
47571      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47572      * listWidth has a higher value)
47573      */
47574     minListWidth : 70,
47575     /**
47576      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47577      * allow the user to set arbitrary text into the field (defaults to false)
47578      */
47579     forceSelection:false,
47580     /**
47581      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47582      * if typeAhead = true (defaults to 250)
47583      */
47584     typeAheadDelay : 250,
47585     /**
47586      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47587      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47588      */
47589     valueNotFoundText : undefined,
47590     
47591     /**
47592      * @cfg {String} defaultValue The value displayed after loading the store.
47593      */
47594     defaultValue: '',
47595     
47596     /**
47597      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47598      */
47599     blockFocus : false,
47600     
47601     /**
47602      * @cfg {Boolean} disableClear Disable showing of clear button.
47603      */
47604     disableClear : false,
47605     /**
47606      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47607      */
47608     alwaysQuery : false,
47609     
47610     //private
47611     addicon : false,
47612     editicon: false,
47613     
47614     // element that contains real text value.. (when hidden is used..)
47615      
47616     // private
47617     onRender : function(ct, position){
47618         Roo.form.Field.prototype.onRender.call(this, ct, position);
47619         
47620         if(this.store){
47621             this.store.on('beforeload', this.onBeforeLoad, this);
47622             this.store.on('load', this.onLoad, this);
47623             this.store.on('loadexception', this.onLoadException, this);
47624             this.store.load({});
47625         }
47626         
47627         
47628         
47629     },
47630
47631     // private
47632     initEvents : function(){
47633         //Roo.form.ComboBox.superclass.initEvents.call(this);
47634  
47635     },
47636
47637     onDestroy : function(){
47638        
47639         if(this.store){
47640             this.store.un('beforeload', this.onBeforeLoad, this);
47641             this.store.un('load', this.onLoad, this);
47642             this.store.un('loadexception', this.onLoadException, this);
47643         }
47644         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47645     },
47646
47647     // private
47648     fireKey : function(e){
47649         if(e.isNavKeyPress() && !this.list.isVisible()){
47650             this.fireEvent("specialkey", this, e);
47651         }
47652     },
47653
47654     // private
47655     onResize: function(w, h){
47656         
47657         return; 
47658     
47659         
47660     },
47661
47662     /**
47663      * Allow or prevent the user from directly editing the field text.  If false is passed,
47664      * the user will only be able to select from the items defined in the dropdown list.  This method
47665      * is the runtime equivalent of setting the 'editable' config option at config time.
47666      * @param {Boolean} value True to allow the user to directly edit the field text
47667      */
47668     setEditable : function(value){
47669          
47670     },
47671
47672     // private
47673     onBeforeLoad : function(){
47674         
47675         Roo.log("Select before load");
47676         return;
47677     
47678         this.innerList.update(this.loadingText ?
47679                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47680         //this.restrictHeight();
47681         this.selectedIndex = -1;
47682     },
47683
47684     // private
47685     onLoad : function(){
47686
47687     
47688         var dom = this.el.dom;
47689         dom.innerHTML = '';
47690          var od = dom.ownerDocument;
47691          
47692         if (this.emptyText) {
47693             var op = od.createElement('option');
47694             op.setAttribute('value', '');
47695             op.innerHTML = String.format('{0}', this.emptyText);
47696             dom.appendChild(op);
47697         }
47698         if(this.store.getCount() > 0){
47699            
47700             var vf = this.valueField;
47701             var df = this.displayField;
47702             this.store.data.each(function(r) {
47703                 // which colmsn to use... testing - cdoe / title..
47704                 var op = od.createElement('option');
47705                 op.setAttribute('value', r.data[vf]);
47706                 op.innerHTML = String.format('{0}', r.data[df]);
47707                 dom.appendChild(op);
47708             });
47709             if (typeof(this.defaultValue != 'undefined')) {
47710                 this.setValue(this.defaultValue);
47711             }
47712             
47713              
47714         }else{
47715             //this.onEmptyResults();
47716         }
47717         //this.el.focus();
47718     },
47719     // private
47720     onLoadException : function()
47721     {
47722         dom.innerHTML = '';
47723             
47724         Roo.log("Select on load exception");
47725         return;
47726     
47727         this.collapse();
47728         Roo.log(this.store.reader.jsonData);
47729         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47730             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47731         }
47732         
47733         
47734     },
47735     // private
47736     onTypeAhead : function(){
47737          
47738     },
47739
47740     // private
47741     onSelect : function(record, index){
47742         Roo.log('on select?');
47743         return;
47744         if(this.fireEvent('beforeselect', this, record, index) !== false){
47745             this.setFromData(index > -1 ? record.data : false);
47746             this.collapse();
47747             this.fireEvent('select', this, record, index);
47748         }
47749     },
47750
47751     /**
47752      * Returns the currently selected field value or empty string if no value is set.
47753      * @return {String} value The selected value
47754      */
47755     getValue : function(){
47756         var dom = this.el.dom;
47757         this.value = dom.options[dom.selectedIndex].value;
47758         return this.value;
47759         
47760     },
47761
47762     /**
47763      * Clears any text/value currently set in the field
47764      */
47765     clearValue : function(){
47766         this.value = '';
47767         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47768         
47769     },
47770
47771     /**
47772      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47773      * will be displayed in the field.  If the value does not match the data value of an existing item,
47774      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47775      * Otherwise the field will be blank (although the value will still be set).
47776      * @param {String} value The value to match
47777      */
47778     setValue : function(v){
47779         var d = this.el.dom;
47780         for (var i =0; i < d.options.length;i++) {
47781             if (v == d.options[i].value) {
47782                 d.selectedIndex = i;
47783                 this.value = v;
47784                 return;
47785             }
47786         }
47787         this.clearValue();
47788     },
47789     /**
47790      * @property {Object} the last set data for the element
47791      */
47792     
47793     lastData : false,
47794     /**
47795      * Sets the value of the field based on a object which is related to the record format for the store.
47796      * @param {Object} value the value to set as. or false on reset?
47797      */
47798     setFromData : function(o){
47799         Roo.log('setfrom data?');
47800          
47801         
47802         
47803     },
47804     // private
47805     reset : function(){
47806         this.clearValue();
47807     },
47808     // private
47809     findRecord : function(prop, value){
47810         
47811         return false;
47812     
47813         var record;
47814         if(this.store.getCount() > 0){
47815             this.store.each(function(r){
47816                 if(r.data[prop] == value){
47817                     record = r;
47818                     return false;
47819                 }
47820                 return true;
47821             });
47822         }
47823         return record;
47824     },
47825     
47826     getName: function()
47827     {
47828         // returns hidden if it's set..
47829         if (!this.rendered) {return ''};
47830         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47831         
47832     },
47833      
47834
47835     
47836
47837     // private
47838     onEmptyResults : function(){
47839         Roo.log('empty results');
47840         //this.collapse();
47841     },
47842
47843     /**
47844      * Returns true if the dropdown list is expanded, else false.
47845      */
47846     isExpanded : function(){
47847         return false;
47848     },
47849
47850     /**
47851      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47852      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47853      * @param {String} value The data value of the item to select
47854      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47855      * selected item if it is not currently in view (defaults to true)
47856      * @return {Boolean} True if the value matched an item in the list, else false
47857      */
47858     selectByValue : function(v, scrollIntoView){
47859         Roo.log('select By Value');
47860         return false;
47861     
47862         if(v !== undefined && v !== null){
47863             var r = this.findRecord(this.valueField || this.displayField, v);
47864             if(r){
47865                 this.select(this.store.indexOf(r), scrollIntoView);
47866                 return true;
47867             }
47868         }
47869         return false;
47870     },
47871
47872     /**
47873      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47874      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47875      * @param {Number} index The zero-based index of the list item to select
47876      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47877      * selected item if it is not currently in view (defaults to true)
47878      */
47879     select : function(index, scrollIntoView){
47880         Roo.log('select ');
47881         return  ;
47882         
47883         this.selectedIndex = index;
47884         this.view.select(index);
47885         if(scrollIntoView !== false){
47886             var el = this.view.getNode(index);
47887             if(el){
47888                 this.innerList.scrollChildIntoView(el, false);
47889             }
47890         }
47891     },
47892
47893       
47894
47895     // private
47896     validateBlur : function(){
47897         
47898         return;
47899         
47900     },
47901
47902     // private
47903     initQuery : function(){
47904         this.doQuery(this.getRawValue());
47905     },
47906
47907     // private
47908     doForce : function(){
47909         if(this.el.dom.value.length > 0){
47910             this.el.dom.value =
47911                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47912              
47913         }
47914     },
47915
47916     /**
47917      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47918      * query allowing the query action to be canceled if needed.
47919      * @param {String} query The SQL query to execute
47920      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47921      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47922      * saved in the current store (defaults to false)
47923      */
47924     doQuery : function(q, forceAll){
47925         
47926         Roo.log('doQuery?');
47927         if(q === undefined || q === null){
47928             q = '';
47929         }
47930         var qe = {
47931             query: q,
47932             forceAll: forceAll,
47933             combo: this,
47934             cancel:false
47935         };
47936         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47937             return false;
47938         }
47939         q = qe.query;
47940         forceAll = qe.forceAll;
47941         if(forceAll === true || (q.length >= this.minChars)){
47942             if(this.lastQuery != q || this.alwaysQuery){
47943                 this.lastQuery = q;
47944                 if(this.mode == 'local'){
47945                     this.selectedIndex = -1;
47946                     if(forceAll){
47947                         this.store.clearFilter();
47948                     }else{
47949                         this.store.filter(this.displayField, q);
47950                     }
47951                     this.onLoad();
47952                 }else{
47953                     this.store.baseParams[this.queryParam] = q;
47954                     this.store.load({
47955                         params: this.getParams(q)
47956                     });
47957                     this.expand();
47958                 }
47959             }else{
47960                 this.selectedIndex = -1;
47961                 this.onLoad();   
47962             }
47963         }
47964     },
47965
47966     // private
47967     getParams : function(q){
47968         var p = {};
47969         //p[this.queryParam] = q;
47970         if(this.pageSize){
47971             p.start = 0;
47972             p.limit = this.pageSize;
47973         }
47974         return p;
47975     },
47976
47977     /**
47978      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47979      */
47980     collapse : function(){
47981         
47982     },
47983
47984     // private
47985     collapseIf : function(e){
47986         
47987     },
47988
47989     /**
47990      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47991      */
47992     expand : function(){
47993         
47994     } ,
47995
47996     // private
47997      
47998
47999     /** 
48000     * @cfg {Boolean} grow 
48001     * @hide 
48002     */
48003     /** 
48004     * @cfg {Number} growMin 
48005     * @hide 
48006     */
48007     /** 
48008     * @cfg {Number} growMax 
48009     * @hide 
48010     */
48011     /**
48012      * @hide
48013      * @method autoSize
48014      */
48015     
48016     setWidth : function()
48017     {
48018         
48019     },
48020     getResizeEl : function(){
48021         return this.el;
48022     }
48023 });//<script type="text/javasscript">
48024  
48025
48026 /**
48027  * @class Roo.DDView
48028  * A DnD enabled version of Roo.View.
48029  * @param {Element/String} container The Element in which to create the View.
48030  * @param {String} tpl The template string used to create the markup for each element of the View
48031  * @param {Object} config The configuration properties. These include all the config options of
48032  * {@link Roo.View} plus some specific to this class.<br>
48033  * <p>
48034  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48035  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48036  * <p>
48037  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48038 .x-view-drag-insert-above {
48039         border-top:1px dotted #3366cc;
48040 }
48041 .x-view-drag-insert-below {
48042         border-bottom:1px dotted #3366cc;
48043 }
48044 </code></pre>
48045  * 
48046  */
48047  
48048 Roo.DDView = function(container, tpl, config) {
48049     Roo.DDView.superclass.constructor.apply(this, arguments);
48050     this.getEl().setStyle("outline", "0px none");
48051     this.getEl().unselectable();
48052     if (this.dragGroup) {
48053                 this.setDraggable(this.dragGroup.split(","));
48054     }
48055     if (this.dropGroup) {
48056                 this.setDroppable(this.dropGroup.split(","));
48057     }
48058     if (this.deletable) {
48059         this.setDeletable();
48060     }
48061     this.isDirtyFlag = false;
48062         this.addEvents({
48063                 "drop" : true
48064         });
48065 };
48066
48067 Roo.extend(Roo.DDView, Roo.View, {
48068 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48069 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48070 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48071 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48072
48073         isFormField: true,
48074
48075         reset: Roo.emptyFn,
48076         
48077         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48078
48079         validate: function() {
48080                 return true;
48081         },
48082         
48083         destroy: function() {
48084                 this.purgeListeners();
48085                 this.getEl.removeAllListeners();
48086                 this.getEl().remove();
48087                 if (this.dragZone) {
48088                         if (this.dragZone.destroy) {
48089                                 this.dragZone.destroy();
48090                         }
48091                 }
48092                 if (this.dropZone) {
48093                         if (this.dropZone.destroy) {
48094                                 this.dropZone.destroy();
48095                         }
48096                 }
48097         },
48098
48099 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48100         getName: function() {
48101                 return this.name;
48102         },
48103
48104 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48105         setValue: function(v) {
48106                 if (!this.store) {
48107                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48108                 }
48109                 var data = {};
48110                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48111                 this.store.proxy = new Roo.data.MemoryProxy(data);
48112                 this.store.load();
48113         },
48114
48115 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48116         getValue: function() {
48117                 var result = '(';
48118                 this.store.each(function(rec) {
48119                         result += rec.id + ',';
48120                 });
48121                 return result.substr(0, result.length - 1) + ')';
48122         },
48123         
48124         getIds: function() {
48125                 var i = 0, result = new Array(this.store.getCount());
48126                 this.store.each(function(rec) {
48127                         result[i++] = rec.id;
48128                 });
48129                 return result;
48130         },
48131         
48132         isDirty: function() {
48133                 return this.isDirtyFlag;
48134         },
48135
48136 /**
48137  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48138  *      whole Element becomes the target, and this causes the drop gesture to append.
48139  */
48140     getTargetFromEvent : function(e) {
48141                 var target = e.getTarget();
48142                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48143                 target = target.parentNode;
48144                 }
48145                 if (!target) {
48146                         target = this.el.dom.lastChild || this.el.dom;
48147                 }
48148                 return target;
48149     },
48150
48151 /**
48152  *      Create the drag data which consists of an object which has the property "ddel" as
48153  *      the drag proxy element. 
48154  */
48155     getDragData : function(e) {
48156         var target = this.findItemFromChild(e.getTarget());
48157                 if(target) {
48158                         this.handleSelection(e);
48159                         var selNodes = this.getSelectedNodes();
48160             var dragData = {
48161                 source: this,
48162                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48163                 nodes: selNodes,
48164                 records: []
48165                         };
48166                         var selectedIndices = this.getSelectedIndexes();
48167                         for (var i = 0; i < selectedIndices.length; i++) {
48168                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48169                         }
48170                         if (selNodes.length == 1) {
48171                                 dragData.ddel = target.cloneNode(true); // the div element
48172                         } else {
48173                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48174                                 div.className = 'multi-proxy';
48175                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48176                                         div.appendChild(selNodes[i].cloneNode(true));
48177                                 }
48178                                 dragData.ddel = div;
48179                         }
48180             //console.log(dragData)
48181             //console.log(dragData.ddel.innerHTML)
48182                         return dragData;
48183                 }
48184         //console.log('nodragData')
48185                 return false;
48186     },
48187     
48188 /**     Specify to which ddGroup items in this DDView may be dragged. */
48189     setDraggable: function(ddGroup) {
48190         if (ddGroup instanceof Array) {
48191                 Roo.each(ddGroup, this.setDraggable, this);
48192                 return;
48193         }
48194         if (this.dragZone) {
48195                 this.dragZone.addToGroup(ddGroup);
48196         } else {
48197                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48198                                 containerScroll: true,
48199                                 ddGroup: ddGroup 
48200
48201                         });
48202 //                      Draggability implies selection. DragZone's mousedown selects the element.
48203                         if (!this.multiSelect) { this.singleSelect = true; }
48204
48205 //                      Wire the DragZone's handlers up to methods in *this*
48206                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48207                 }
48208     },
48209
48210 /**     Specify from which ddGroup this DDView accepts drops. */
48211     setDroppable: function(ddGroup) {
48212         if (ddGroup instanceof Array) {
48213                 Roo.each(ddGroup, this.setDroppable, this);
48214                 return;
48215         }
48216         if (this.dropZone) {
48217                 this.dropZone.addToGroup(ddGroup);
48218         } else {
48219                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48220                                 containerScroll: true,
48221                                 ddGroup: ddGroup
48222                         });
48223
48224 //                      Wire the DropZone's handlers up to methods in *this*
48225                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48226                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48227                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48228                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48229                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48230                 }
48231     },
48232
48233 /**     Decide whether to drop above or below a View node. */
48234     getDropPoint : function(e, n, dd){
48235         if (n == this.el.dom) { return "above"; }
48236                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48237                 var c = t + (b - t) / 2;
48238                 var y = Roo.lib.Event.getPageY(e);
48239                 if(y <= c) {
48240                         return "above";
48241                 }else{
48242                         return "below";
48243                 }
48244     },
48245
48246     onNodeEnter : function(n, dd, e, data){
48247                 return false;
48248     },
48249     
48250     onNodeOver : function(n, dd, e, data){
48251                 var pt = this.getDropPoint(e, n, dd);
48252                 // set the insert point style on the target node
48253                 var dragElClass = this.dropNotAllowed;
48254                 if (pt) {
48255                         var targetElClass;
48256                         if (pt == "above"){
48257                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48258                                 targetElClass = "x-view-drag-insert-above";
48259                         } else {
48260                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48261                                 targetElClass = "x-view-drag-insert-below";
48262                         }
48263                         if (this.lastInsertClass != targetElClass){
48264                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48265                                 this.lastInsertClass = targetElClass;
48266                         }
48267                 }
48268                 return dragElClass;
48269         },
48270
48271     onNodeOut : function(n, dd, e, data){
48272                 this.removeDropIndicators(n);
48273     },
48274
48275     onNodeDrop : function(n, dd, e, data){
48276         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48277                 return false;
48278         }
48279         var pt = this.getDropPoint(e, n, dd);
48280                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48281                 if (pt == "below") { insertAt++; }
48282                 for (var i = 0; i < data.records.length; i++) {
48283                         var r = data.records[i];
48284                         var dup = this.store.getById(r.id);
48285                         if (dup && (dd != this.dragZone)) {
48286                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48287                         } else {
48288                                 if (data.copy) {
48289                                         this.store.insert(insertAt++, r.copy());
48290                                 } else {
48291                                         data.source.isDirtyFlag = true;
48292                                         r.store.remove(r);
48293                                         this.store.insert(insertAt++, r);
48294                                 }
48295                                 this.isDirtyFlag = true;
48296                         }
48297                 }
48298                 this.dragZone.cachedTarget = null;
48299                 return true;
48300     },
48301
48302     removeDropIndicators : function(n){
48303                 if(n){
48304                         Roo.fly(n).removeClass([
48305                                 "x-view-drag-insert-above",
48306                                 "x-view-drag-insert-below"]);
48307                         this.lastInsertClass = "_noclass";
48308                 }
48309     },
48310
48311 /**
48312  *      Utility method. Add a delete option to the DDView's context menu.
48313  *      @param {String} imageUrl The URL of the "delete" icon image.
48314  */
48315         setDeletable: function(imageUrl) {
48316                 if (!this.singleSelect && !this.multiSelect) {
48317                         this.singleSelect = true;
48318                 }
48319                 var c = this.getContextMenu();
48320                 this.contextMenu.on("itemclick", function(item) {
48321                         switch (item.id) {
48322                                 case "delete":
48323                                         this.remove(this.getSelectedIndexes());
48324                                         break;
48325                         }
48326                 }, this);
48327                 this.contextMenu.add({
48328                         icon: imageUrl,
48329                         id: "delete",
48330                         text: 'Delete'
48331                 });
48332         },
48333         
48334 /**     Return the context menu for this DDView. */
48335         getContextMenu: function() {
48336                 if (!this.contextMenu) {
48337 //                      Create the View's context menu
48338                         this.contextMenu = new Roo.menu.Menu({
48339                                 id: this.id + "-contextmenu"
48340                         });
48341                         this.el.on("contextmenu", this.showContextMenu, this);
48342                 }
48343                 return this.contextMenu;
48344         },
48345         
48346         disableContextMenu: function() {
48347                 if (this.contextMenu) {
48348                         this.el.un("contextmenu", this.showContextMenu, this);
48349                 }
48350         },
48351
48352         showContextMenu: function(e, item) {
48353         item = this.findItemFromChild(e.getTarget());
48354                 if (item) {
48355                         e.stopEvent();
48356                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48357                         this.contextMenu.showAt(e.getXY());
48358             }
48359     },
48360
48361 /**
48362  *      Remove {@link Roo.data.Record}s at the specified indices.
48363  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48364  */
48365     remove: function(selectedIndices) {
48366                 selectedIndices = [].concat(selectedIndices);
48367                 for (var i = 0; i < selectedIndices.length; i++) {
48368                         var rec = this.store.getAt(selectedIndices[i]);
48369                         this.store.remove(rec);
48370                 }
48371     },
48372
48373 /**
48374  *      Double click fires the event, but also, if this is draggable, and there is only one other
48375  *      related DropZone, it transfers the selected node.
48376  */
48377     onDblClick : function(e){
48378         var item = this.findItemFromChild(e.getTarget());
48379         if(item){
48380             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48381                 return false;
48382             }
48383             if (this.dragGroup) {
48384                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48385                     while (targets.indexOf(this.dropZone) > -1) {
48386                             targets.remove(this.dropZone);
48387                                 }
48388                     if (targets.length == 1) {
48389                                         this.dragZone.cachedTarget = null;
48390                         var el = Roo.get(targets[0].getEl());
48391                         var box = el.getBox(true);
48392                         targets[0].onNodeDrop(el.dom, {
48393                                 target: el.dom,
48394                                 xy: [box.x, box.y + box.height - 1]
48395                         }, null, this.getDragData(e));
48396                     }
48397                 }
48398         }
48399     },
48400     
48401     handleSelection: function(e) {
48402                 this.dragZone.cachedTarget = null;
48403         var item = this.findItemFromChild(e.getTarget());
48404         if (!item) {
48405                 this.clearSelections(true);
48406                 return;
48407         }
48408                 if (item && (this.multiSelect || this.singleSelect)){
48409                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48410                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48411                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48412                                 this.unselect(item);
48413                         } else {
48414                                 this.select(item, this.multiSelect && e.ctrlKey);
48415                                 this.lastSelection = item;
48416                         }
48417                 }
48418     },
48419
48420     onItemClick : function(item, index, e){
48421                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48422                         return false;
48423                 }
48424                 return true;
48425     },
48426
48427     unselect : function(nodeInfo, suppressEvent){
48428                 var node = this.getNode(nodeInfo);
48429                 if(node && this.isSelected(node)){
48430                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48431                                 Roo.fly(node).removeClass(this.selectedClass);
48432                                 this.selections.remove(node);
48433                                 if(!suppressEvent){
48434                                         this.fireEvent("selectionchange", this, this.selections);
48435                                 }
48436                         }
48437                 }
48438     }
48439 });
48440 /*
48441  * Based on:
48442  * Ext JS Library 1.1.1
48443  * Copyright(c) 2006-2007, Ext JS, LLC.
48444  *
48445  * Originally Released Under LGPL - original licence link has changed is not relivant.
48446  *
48447  * Fork - LGPL
48448  * <script type="text/javascript">
48449  */
48450  
48451 /**
48452  * @class Roo.LayoutManager
48453  * @extends Roo.util.Observable
48454  * Base class for layout managers.
48455  */
48456 Roo.LayoutManager = function(container, config){
48457     Roo.LayoutManager.superclass.constructor.call(this);
48458     this.el = Roo.get(container);
48459     // ie scrollbar fix
48460     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48461         document.body.scroll = "no";
48462     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48463         this.el.position('relative');
48464     }
48465     this.id = this.el.id;
48466     this.el.addClass("x-layout-container");
48467     /** false to disable window resize monitoring @type Boolean */
48468     this.monitorWindowResize = true;
48469     this.regions = {};
48470     this.addEvents({
48471         /**
48472          * @event layout
48473          * Fires when a layout is performed. 
48474          * @param {Roo.LayoutManager} this
48475          */
48476         "layout" : true,
48477         /**
48478          * @event regionresized
48479          * Fires when the user resizes a region. 
48480          * @param {Roo.LayoutRegion} region The resized region
48481          * @param {Number} newSize The new size (width for east/west, height for north/south)
48482          */
48483         "regionresized" : true,
48484         /**
48485          * @event regioncollapsed
48486          * Fires when a region is collapsed. 
48487          * @param {Roo.LayoutRegion} region The collapsed region
48488          */
48489         "regioncollapsed" : true,
48490         /**
48491          * @event regionexpanded
48492          * Fires when a region is expanded.  
48493          * @param {Roo.LayoutRegion} region The expanded region
48494          */
48495         "regionexpanded" : true
48496     });
48497     this.updating = false;
48498     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48499 };
48500
48501 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48502     /**
48503      * Returns true if this layout is currently being updated
48504      * @return {Boolean}
48505      */
48506     isUpdating : function(){
48507         return this.updating; 
48508     },
48509     
48510     /**
48511      * Suspend the LayoutManager from doing auto-layouts while
48512      * making multiple add or remove calls
48513      */
48514     beginUpdate : function(){
48515         this.updating = true;    
48516     },
48517     
48518     /**
48519      * Restore auto-layouts and optionally disable the manager from performing a layout
48520      * @param {Boolean} noLayout true to disable a layout update 
48521      */
48522     endUpdate : function(noLayout){
48523         this.updating = false;
48524         if(!noLayout){
48525             this.layout();
48526         }    
48527     },
48528     
48529     layout: function(){
48530         
48531     },
48532     
48533     onRegionResized : function(region, newSize){
48534         this.fireEvent("regionresized", region, newSize);
48535         this.layout();
48536     },
48537     
48538     onRegionCollapsed : function(region){
48539         this.fireEvent("regioncollapsed", region);
48540     },
48541     
48542     onRegionExpanded : function(region){
48543         this.fireEvent("regionexpanded", region);
48544     },
48545         
48546     /**
48547      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48548      * performs box-model adjustments.
48549      * @return {Object} The size as an object {width: (the width), height: (the height)}
48550      */
48551     getViewSize : function(){
48552         var size;
48553         if(this.el.dom != document.body){
48554             size = this.el.getSize();
48555         }else{
48556             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48557         }
48558         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48559         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48560         return size;
48561     },
48562     
48563     /**
48564      * Returns the Element this layout is bound to.
48565      * @return {Roo.Element}
48566      */
48567     getEl : function(){
48568         return this.el;
48569     },
48570     
48571     /**
48572      * Returns the specified region.
48573      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48574      * @return {Roo.LayoutRegion}
48575      */
48576     getRegion : function(target){
48577         return this.regions[target.toLowerCase()];
48578     },
48579     
48580     onWindowResize : function(){
48581         if(this.monitorWindowResize){
48582             this.layout();
48583         }
48584     }
48585 });/*
48586  * Based on:
48587  * Ext JS Library 1.1.1
48588  * Copyright(c) 2006-2007, Ext JS, LLC.
48589  *
48590  * Originally Released Under LGPL - original licence link has changed is not relivant.
48591  *
48592  * Fork - LGPL
48593  * <script type="text/javascript">
48594  */
48595 /**
48596  * @class Roo.BorderLayout
48597  * @extends Roo.LayoutManager
48598  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48599  * please see: <br><br>
48600  * <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>
48601  * <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>
48602  * Example:
48603  <pre><code>
48604  var layout = new Roo.BorderLayout(document.body, {
48605     north: {
48606         initialSize: 25,
48607         titlebar: false
48608     },
48609     west: {
48610         split:true,
48611         initialSize: 200,
48612         minSize: 175,
48613         maxSize: 400,
48614         titlebar: true,
48615         collapsible: true
48616     },
48617     east: {
48618         split:true,
48619         initialSize: 202,
48620         minSize: 175,
48621         maxSize: 400,
48622         titlebar: true,
48623         collapsible: true
48624     },
48625     south: {
48626         split:true,
48627         initialSize: 100,
48628         minSize: 100,
48629         maxSize: 200,
48630         titlebar: true,
48631         collapsible: true
48632     },
48633     center: {
48634         titlebar: true,
48635         autoScroll:true,
48636         resizeTabs: true,
48637         minTabWidth: 50,
48638         preferredTabWidth: 150
48639     }
48640 });
48641
48642 // shorthand
48643 var CP = Roo.ContentPanel;
48644
48645 layout.beginUpdate();
48646 layout.add("north", new CP("north", "North"));
48647 layout.add("south", new CP("south", {title: "South", closable: true}));
48648 layout.add("west", new CP("west", {title: "West"}));
48649 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48650 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48651 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48652 layout.getRegion("center").showPanel("center1");
48653 layout.endUpdate();
48654 </code></pre>
48655
48656 <b>The container the layout is rendered into can be either the body element or any other element.
48657 If it is not the body element, the container needs to either be an absolute positioned element,
48658 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48659 the container size if it is not the body element.</b>
48660
48661 * @constructor
48662 * Create a new BorderLayout
48663 * @param {String/HTMLElement/Element} container The container this layout is bound to
48664 * @param {Object} config Configuration options
48665  */
48666 Roo.BorderLayout = function(container, config){
48667     config = config || {};
48668     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48669     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48670     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48671         var target = this.factory.validRegions[i];
48672         if(config[target]){
48673             this.addRegion(target, config[target]);
48674         }
48675     }
48676 };
48677
48678 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48679     /**
48680      * Creates and adds a new region if it doesn't already exist.
48681      * @param {String} target The target region key (north, south, east, west or center).
48682      * @param {Object} config The regions config object
48683      * @return {BorderLayoutRegion} The new region
48684      */
48685     addRegion : function(target, config){
48686         if(!this.regions[target]){
48687             var r = this.factory.create(target, this, config);
48688             this.bindRegion(target, r);
48689         }
48690         return this.regions[target];
48691     },
48692
48693     // private (kinda)
48694     bindRegion : function(name, r){
48695         this.regions[name] = r;
48696         r.on("visibilitychange", this.layout, this);
48697         r.on("paneladded", this.layout, this);
48698         r.on("panelremoved", this.layout, this);
48699         r.on("invalidated", this.layout, this);
48700         r.on("resized", this.onRegionResized, this);
48701         r.on("collapsed", this.onRegionCollapsed, this);
48702         r.on("expanded", this.onRegionExpanded, this);
48703     },
48704
48705     /**
48706      * Performs a layout update.
48707      */
48708     layout : function(){
48709         if(this.updating) return;
48710         var size = this.getViewSize();
48711         var w = size.width;
48712         var h = size.height;
48713         var centerW = w;
48714         var centerH = h;
48715         var centerY = 0;
48716         var centerX = 0;
48717         //var x = 0, y = 0;
48718
48719         var rs = this.regions;
48720         var north = rs["north"];
48721         var south = rs["south"]; 
48722         var west = rs["west"];
48723         var east = rs["east"];
48724         var center = rs["center"];
48725         //if(this.hideOnLayout){ // not supported anymore
48726             //c.el.setStyle("display", "none");
48727         //}
48728         if(north && north.isVisible()){
48729             var b = north.getBox();
48730             var m = north.getMargins();
48731             b.width = w - (m.left+m.right);
48732             b.x = m.left;
48733             b.y = m.top;
48734             centerY = b.height + b.y + m.bottom;
48735             centerH -= centerY;
48736             north.updateBox(this.safeBox(b));
48737         }
48738         if(south && south.isVisible()){
48739             var b = south.getBox();
48740             var m = south.getMargins();
48741             b.width = w - (m.left+m.right);
48742             b.x = m.left;
48743             var totalHeight = (b.height + m.top + m.bottom);
48744             b.y = h - totalHeight + m.top;
48745             centerH -= totalHeight;
48746             south.updateBox(this.safeBox(b));
48747         }
48748         if(west && west.isVisible()){
48749             var b = west.getBox();
48750             var m = west.getMargins();
48751             b.height = centerH - (m.top+m.bottom);
48752             b.x = m.left;
48753             b.y = centerY + m.top;
48754             var totalWidth = (b.width + m.left + m.right);
48755             centerX += totalWidth;
48756             centerW -= totalWidth;
48757             west.updateBox(this.safeBox(b));
48758         }
48759         if(east && east.isVisible()){
48760             var b = east.getBox();
48761             var m = east.getMargins();
48762             b.height = centerH - (m.top+m.bottom);
48763             var totalWidth = (b.width + m.left + m.right);
48764             b.x = w - totalWidth + m.left;
48765             b.y = centerY + m.top;
48766             centerW -= totalWidth;
48767             east.updateBox(this.safeBox(b));
48768         }
48769         if(center){
48770             var m = center.getMargins();
48771             var centerBox = {
48772                 x: centerX + m.left,
48773                 y: centerY + m.top,
48774                 width: centerW - (m.left+m.right),
48775                 height: centerH - (m.top+m.bottom)
48776             };
48777             //if(this.hideOnLayout){
48778                 //center.el.setStyle("display", "block");
48779             //}
48780             center.updateBox(this.safeBox(centerBox));
48781         }
48782         this.el.repaint();
48783         this.fireEvent("layout", this);
48784     },
48785
48786     // private
48787     safeBox : function(box){
48788         box.width = Math.max(0, box.width);
48789         box.height = Math.max(0, box.height);
48790         return box;
48791     },
48792
48793     /**
48794      * Adds a ContentPanel (or subclass) to this layout.
48795      * @param {String} target The target region key (north, south, east, west or center).
48796      * @param {Roo.ContentPanel} panel The panel to add
48797      * @return {Roo.ContentPanel} The added panel
48798      */
48799     add : function(target, panel){
48800          
48801         target = target.toLowerCase();
48802         return this.regions[target].add(panel);
48803     },
48804
48805     /**
48806      * Remove a ContentPanel (or subclass) to this layout.
48807      * @param {String} target The target region key (north, south, east, west or center).
48808      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48809      * @return {Roo.ContentPanel} The removed panel
48810      */
48811     remove : function(target, panel){
48812         target = target.toLowerCase();
48813         return this.regions[target].remove(panel);
48814     },
48815
48816     /**
48817      * Searches all regions for a panel with the specified id
48818      * @param {String} panelId
48819      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48820      */
48821     findPanel : function(panelId){
48822         var rs = this.regions;
48823         for(var target in rs){
48824             if(typeof rs[target] != "function"){
48825                 var p = rs[target].getPanel(panelId);
48826                 if(p){
48827                     return p;
48828                 }
48829             }
48830         }
48831         return null;
48832     },
48833
48834     /**
48835      * Searches all regions for a panel with the specified id and activates (shows) it.
48836      * @param {String/ContentPanel} panelId The panels id or the panel itself
48837      * @return {Roo.ContentPanel} The shown panel or null
48838      */
48839     showPanel : function(panelId) {
48840       var rs = this.regions;
48841       for(var target in rs){
48842          var r = rs[target];
48843          if(typeof r != "function"){
48844             if(r.hasPanel(panelId)){
48845                return r.showPanel(panelId);
48846             }
48847          }
48848       }
48849       return null;
48850    },
48851
48852    /**
48853      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48854      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48855      */
48856     restoreState : function(provider){
48857         if(!provider){
48858             provider = Roo.state.Manager;
48859         }
48860         var sm = new Roo.LayoutStateManager();
48861         sm.init(this, provider);
48862     },
48863
48864     /**
48865      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48866      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48867      * a valid ContentPanel config object.  Example:
48868      * <pre><code>
48869 // Create the main layout
48870 var layout = new Roo.BorderLayout('main-ct', {
48871     west: {
48872         split:true,
48873         minSize: 175,
48874         titlebar: true
48875     },
48876     center: {
48877         title:'Components'
48878     }
48879 }, 'main-ct');
48880
48881 // Create and add multiple ContentPanels at once via configs
48882 layout.batchAdd({
48883    west: {
48884        id: 'source-files',
48885        autoCreate:true,
48886        title:'Ext Source Files',
48887        autoScroll:true,
48888        fitToFrame:true
48889    },
48890    center : {
48891        el: cview,
48892        autoScroll:true,
48893        fitToFrame:true,
48894        toolbar: tb,
48895        resizeEl:'cbody'
48896    }
48897 });
48898 </code></pre>
48899      * @param {Object} regions An object containing ContentPanel configs by region name
48900      */
48901     batchAdd : function(regions){
48902         this.beginUpdate();
48903         for(var rname in regions){
48904             var lr = this.regions[rname];
48905             if(lr){
48906                 this.addTypedPanels(lr, regions[rname]);
48907             }
48908         }
48909         this.endUpdate();
48910     },
48911
48912     // private
48913     addTypedPanels : function(lr, ps){
48914         if(typeof ps == 'string'){
48915             lr.add(new Roo.ContentPanel(ps));
48916         }
48917         else if(ps instanceof Array){
48918             for(var i =0, len = ps.length; i < len; i++){
48919                 this.addTypedPanels(lr, ps[i]);
48920             }
48921         }
48922         else if(!ps.events){ // raw config?
48923             var el = ps.el;
48924             delete ps.el; // prevent conflict
48925             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48926         }
48927         else {  // panel object assumed!
48928             lr.add(ps);
48929         }
48930     },
48931     /**
48932      * Adds a xtype elements to the layout.
48933      * <pre><code>
48934
48935 layout.addxtype({
48936        xtype : 'ContentPanel',
48937        region: 'west',
48938        items: [ .... ]
48939    }
48940 );
48941
48942 layout.addxtype({
48943         xtype : 'NestedLayoutPanel',
48944         region: 'west',
48945         layout: {
48946            center: { },
48947            west: { }   
48948         },
48949         items : [ ... list of content panels or nested layout panels.. ]
48950    }
48951 );
48952 </code></pre>
48953      * @param {Object} cfg Xtype definition of item to add.
48954      */
48955     addxtype : function(cfg)
48956     {
48957         // basically accepts a pannel...
48958         // can accept a layout region..!?!?
48959         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48960         
48961         if (!cfg.xtype.match(/Panel$/)) {
48962             return false;
48963         }
48964         var ret = false;
48965         
48966         if (typeof(cfg.region) == 'undefined') {
48967             Roo.log("Failed to add Panel, region was not set");
48968             Roo.log(cfg);
48969             return false;
48970         }
48971         var region = cfg.region;
48972         delete cfg.region;
48973         
48974           
48975         var xitems = [];
48976         if (cfg.items) {
48977             xitems = cfg.items;
48978             delete cfg.items;
48979         }
48980         var nb = false;
48981         
48982         switch(cfg.xtype) 
48983         {
48984             case 'ContentPanel':  // ContentPanel (el, cfg)
48985             case 'ScrollPanel':  // ContentPanel (el, cfg)
48986             case 'ViewPanel': 
48987                 if(cfg.autoCreate) {
48988                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48989                 } else {
48990                     var el = this.el.createChild();
48991                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48992                 }
48993                 
48994                 this.add(region, ret);
48995                 break;
48996             
48997             
48998             case 'TreePanel': // our new panel!
48999                 cfg.el = this.el.createChild();
49000                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49001                 this.add(region, ret);
49002                 break;
49003             
49004             case 'NestedLayoutPanel': 
49005                 // create a new Layout (which is  a Border Layout...
49006                 var el = this.el.createChild();
49007                 var clayout = cfg.layout;
49008                 delete cfg.layout;
49009                 clayout.items   = clayout.items  || [];
49010                 // replace this exitems with the clayout ones..
49011                 xitems = clayout.items;
49012                  
49013                 
49014                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49015                     cfg.background = false;
49016                 }
49017                 var layout = new Roo.BorderLayout(el, clayout);
49018                 
49019                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49020                 //console.log('adding nested layout panel '  + cfg.toSource());
49021                 this.add(region, ret);
49022                 nb = {}; /// find first...
49023                 break;
49024                 
49025             case 'GridPanel': 
49026             
49027                 // needs grid and region
49028                 
49029                 //var el = this.getRegion(region).el.createChild();
49030                 var el = this.el.createChild();
49031                 // create the grid first...
49032                 
49033                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49034                 delete cfg.grid;
49035                 if (region == 'center' && this.active ) {
49036                     cfg.background = false;
49037                 }
49038                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49039                 
49040                 this.add(region, ret);
49041                 if (cfg.background) {
49042                     ret.on('activate', function(gp) {
49043                         if (!gp.grid.rendered) {
49044                             gp.grid.render();
49045                         }
49046                     });
49047                 } else {
49048                     grid.render();
49049                 }
49050                 break;
49051            
49052            
49053            
49054                 
49055                 
49056                 
49057             default:
49058                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49059                     
49060                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49061                     this.add(region, ret);
49062                 } else {
49063                 
49064                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49065                     return null;
49066                 }
49067                 
49068              // GridPanel (grid, cfg)
49069             
49070         }
49071         this.beginUpdate();
49072         // add children..
49073         var region = '';
49074         var abn = {};
49075         Roo.each(xitems, function(i)  {
49076             region = nb && i.region ? i.region : false;
49077             
49078             var add = ret.addxtype(i);
49079            
49080             if (region) {
49081                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49082                 if (!i.background) {
49083                     abn[region] = nb[region] ;
49084                 }
49085             }
49086             
49087         });
49088         this.endUpdate();
49089
49090         // make the last non-background panel active..
49091         //if (nb) { Roo.log(abn); }
49092         if (nb) {
49093             
49094             for(var r in abn) {
49095                 region = this.getRegion(r);
49096                 if (region) {
49097                     // tried using nb[r], but it does not work..
49098                      
49099                     region.showPanel(abn[r]);
49100                    
49101                 }
49102             }
49103         }
49104         return ret;
49105         
49106     }
49107 });
49108
49109 /**
49110  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49111  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49112  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49113  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49114  * <pre><code>
49115 // shorthand
49116 var CP = Roo.ContentPanel;
49117
49118 var layout = Roo.BorderLayout.create({
49119     north: {
49120         initialSize: 25,
49121         titlebar: false,
49122         panels: [new CP("north", "North")]
49123     },
49124     west: {
49125         split:true,
49126         initialSize: 200,
49127         minSize: 175,
49128         maxSize: 400,
49129         titlebar: true,
49130         collapsible: true,
49131         panels: [new CP("west", {title: "West"})]
49132     },
49133     east: {
49134         split:true,
49135         initialSize: 202,
49136         minSize: 175,
49137         maxSize: 400,
49138         titlebar: true,
49139         collapsible: true,
49140         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49141     },
49142     south: {
49143         split:true,
49144         initialSize: 100,
49145         minSize: 100,
49146         maxSize: 200,
49147         titlebar: true,
49148         collapsible: true,
49149         panels: [new CP("south", {title: "South", closable: true})]
49150     },
49151     center: {
49152         titlebar: true,
49153         autoScroll:true,
49154         resizeTabs: true,
49155         minTabWidth: 50,
49156         preferredTabWidth: 150,
49157         panels: [
49158             new CP("center1", {title: "Close Me", closable: true}),
49159             new CP("center2", {title: "Center Panel", closable: false})
49160         ]
49161     }
49162 }, document.body);
49163
49164 layout.getRegion("center").showPanel("center1");
49165 </code></pre>
49166  * @param config
49167  * @param targetEl
49168  */
49169 Roo.BorderLayout.create = function(config, targetEl){
49170     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49171     layout.beginUpdate();
49172     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49173     for(var j = 0, jlen = regions.length; j < jlen; j++){
49174         var lr = regions[j];
49175         if(layout.regions[lr] && config[lr].panels){
49176             var r = layout.regions[lr];
49177             var ps = config[lr].panels;
49178             layout.addTypedPanels(r, ps);
49179         }
49180     }
49181     layout.endUpdate();
49182     return layout;
49183 };
49184
49185 // private
49186 Roo.BorderLayout.RegionFactory = {
49187     // private
49188     validRegions : ["north","south","east","west","center"],
49189
49190     // private
49191     create : function(target, mgr, config){
49192         target = target.toLowerCase();
49193         if(config.lightweight || config.basic){
49194             return new Roo.BasicLayoutRegion(mgr, config, target);
49195         }
49196         switch(target){
49197             case "north":
49198                 return new Roo.NorthLayoutRegion(mgr, config);
49199             case "south":
49200                 return new Roo.SouthLayoutRegion(mgr, config);
49201             case "east":
49202                 return new Roo.EastLayoutRegion(mgr, config);
49203             case "west":
49204                 return new Roo.WestLayoutRegion(mgr, config);
49205             case "center":
49206                 return new Roo.CenterLayoutRegion(mgr, config);
49207         }
49208         throw 'Layout region "'+target+'" not supported.';
49209     }
49210 };/*
49211  * Based on:
49212  * Ext JS Library 1.1.1
49213  * Copyright(c) 2006-2007, Ext JS, LLC.
49214  *
49215  * Originally Released Under LGPL - original licence link has changed is not relivant.
49216  *
49217  * Fork - LGPL
49218  * <script type="text/javascript">
49219  */
49220  
49221 /**
49222  * @class Roo.BasicLayoutRegion
49223  * @extends Roo.util.Observable
49224  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49225  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49226  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49227  */
49228 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49229     this.mgr = mgr;
49230     this.position  = pos;
49231     this.events = {
49232         /**
49233          * @scope Roo.BasicLayoutRegion
49234          */
49235         
49236         /**
49237          * @event beforeremove
49238          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49239          * @param {Roo.LayoutRegion} this
49240          * @param {Roo.ContentPanel} panel The panel
49241          * @param {Object} e The cancel event object
49242          */
49243         "beforeremove" : true,
49244         /**
49245          * @event invalidated
49246          * Fires when the layout for this region is changed.
49247          * @param {Roo.LayoutRegion} this
49248          */
49249         "invalidated" : true,
49250         /**
49251          * @event visibilitychange
49252          * Fires when this region is shown or hidden 
49253          * @param {Roo.LayoutRegion} this
49254          * @param {Boolean} visibility true or false
49255          */
49256         "visibilitychange" : true,
49257         /**
49258          * @event paneladded
49259          * Fires when a panel is added. 
49260          * @param {Roo.LayoutRegion} this
49261          * @param {Roo.ContentPanel} panel The panel
49262          */
49263         "paneladded" : true,
49264         /**
49265          * @event panelremoved
49266          * Fires when a panel is removed. 
49267          * @param {Roo.LayoutRegion} this
49268          * @param {Roo.ContentPanel} panel The panel
49269          */
49270         "panelremoved" : true,
49271         /**
49272          * @event collapsed
49273          * Fires when this region is collapsed.
49274          * @param {Roo.LayoutRegion} this
49275          */
49276         "collapsed" : true,
49277         /**
49278          * @event expanded
49279          * Fires when this region is expanded.
49280          * @param {Roo.LayoutRegion} this
49281          */
49282         "expanded" : true,
49283         /**
49284          * @event slideshow
49285          * Fires when this region is slid into view.
49286          * @param {Roo.LayoutRegion} this
49287          */
49288         "slideshow" : true,
49289         /**
49290          * @event slidehide
49291          * Fires when this region slides out of view. 
49292          * @param {Roo.LayoutRegion} this
49293          */
49294         "slidehide" : true,
49295         /**
49296          * @event panelactivated
49297          * Fires when a panel is activated. 
49298          * @param {Roo.LayoutRegion} this
49299          * @param {Roo.ContentPanel} panel The activated panel
49300          */
49301         "panelactivated" : true,
49302         /**
49303          * @event resized
49304          * Fires when the user resizes this region. 
49305          * @param {Roo.LayoutRegion} this
49306          * @param {Number} newSize The new size (width for east/west, height for north/south)
49307          */
49308         "resized" : true
49309     };
49310     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49311     this.panels = new Roo.util.MixedCollection();
49312     this.panels.getKey = this.getPanelId.createDelegate(this);
49313     this.box = null;
49314     this.activePanel = null;
49315     // ensure listeners are added...
49316     
49317     if (config.listeners || config.events) {
49318         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49319             listeners : config.listeners || {},
49320             events : config.events || {}
49321         });
49322     }
49323     
49324     if(skipConfig !== true){
49325         this.applyConfig(config);
49326     }
49327 };
49328
49329 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49330     getPanelId : function(p){
49331         return p.getId();
49332     },
49333     
49334     applyConfig : function(config){
49335         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49336         this.config = config;
49337         
49338     },
49339     
49340     /**
49341      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49342      * the width, for horizontal (north, south) the height.
49343      * @param {Number} newSize The new width or height
49344      */
49345     resizeTo : function(newSize){
49346         var el = this.el ? this.el :
49347                  (this.activePanel ? this.activePanel.getEl() : null);
49348         if(el){
49349             switch(this.position){
49350                 case "east":
49351                 case "west":
49352                     el.setWidth(newSize);
49353                     this.fireEvent("resized", this, newSize);
49354                 break;
49355                 case "north":
49356                 case "south":
49357                     el.setHeight(newSize);
49358                     this.fireEvent("resized", this, newSize);
49359                 break;                
49360             }
49361         }
49362     },
49363     
49364     getBox : function(){
49365         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49366     },
49367     
49368     getMargins : function(){
49369         return this.margins;
49370     },
49371     
49372     updateBox : function(box){
49373         this.box = box;
49374         var el = this.activePanel.getEl();
49375         el.dom.style.left = box.x + "px";
49376         el.dom.style.top = box.y + "px";
49377         this.activePanel.setSize(box.width, box.height);
49378     },
49379     
49380     /**
49381      * Returns the container element for this region.
49382      * @return {Roo.Element}
49383      */
49384     getEl : function(){
49385         return this.activePanel;
49386     },
49387     
49388     /**
49389      * Returns true if this region is currently visible.
49390      * @return {Boolean}
49391      */
49392     isVisible : function(){
49393         return this.activePanel ? true : false;
49394     },
49395     
49396     setActivePanel : function(panel){
49397         panel = this.getPanel(panel);
49398         if(this.activePanel && this.activePanel != panel){
49399             this.activePanel.setActiveState(false);
49400             this.activePanel.getEl().setLeftTop(-10000,-10000);
49401         }
49402         this.activePanel = panel;
49403         panel.setActiveState(true);
49404         if(this.box){
49405             panel.setSize(this.box.width, this.box.height);
49406         }
49407         this.fireEvent("panelactivated", this, panel);
49408         this.fireEvent("invalidated");
49409     },
49410     
49411     /**
49412      * Show the specified panel.
49413      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49414      * @return {Roo.ContentPanel} The shown panel or null
49415      */
49416     showPanel : function(panel){
49417         if(panel = this.getPanel(panel)){
49418             this.setActivePanel(panel);
49419         }
49420         return panel;
49421     },
49422     
49423     /**
49424      * Get the active panel for this region.
49425      * @return {Roo.ContentPanel} The active panel or null
49426      */
49427     getActivePanel : function(){
49428         return this.activePanel;
49429     },
49430     
49431     /**
49432      * Add the passed ContentPanel(s)
49433      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49434      * @return {Roo.ContentPanel} The panel added (if only one was added)
49435      */
49436     add : function(panel){
49437         if(arguments.length > 1){
49438             for(var i = 0, len = arguments.length; i < len; i++) {
49439                 this.add(arguments[i]);
49440             }
49441             return null;
49442         }
49443         if(this.hasPanel(panel)){
49444             this.showPanel(panel);
49445             return panel;
49446         }
49447         var el = panel.getEl();
49448         if(el.dom.parentNode != this.mgr.el.dom){
49449             this.mgr.el.dom.appendChild(el.dom);
49450         }
49451         if(panel.setRegion){
49452             panel.setRegion(this);
49453         }
49454         this.panels.add(panel);
49455         el.setStyle("position", "absolute");
49456         if(!panel.background){
49457             this.setActivePanel(panel);
49458             if(this.config.initialSize && this.panels.getCount()==1){
49459                 this.resizeTo(this.config.initialSize);
49460             }
49461         }
49462         this.fireEvent("paneladded", this, panel);
49463         return panel;
49464     },
49465     
49466     /**
49467      * Returns true if the panel is in this region.
49468      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49469      * @return {Boolean}
49470      */
49471     hasPanel : function(panel){
49472         if(typeof panel == "object"){ // must be panel obj
49473             panel = panel.getId();
49474         }
49475         return this.getPanel(panel) ? true : false;
49476     },
49477     
49478     /**
49479      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49480      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49481      * @param {Boolean} preservePanel Overrides the config preservePanel option
49482      * @return {Roo.ContentPanel} The panel that was removed
49483      */
49484     remove : function(panel, preservePanel){
49485         panel = this.getPanel(panel);
49486         if(!panel){
49487             return null;
49488         }
49489         var e = {};
49490         this.fireEvent("beforeremove", this, panel, e);
49491         if(e.cancel === true){
49492             return null;
49493         }
49494         var panelId = panel.getId();
49495         this.panels.removeKey(panelId);
49496         return panel;
49497     },
49498     
49499     /**
49500      * Returns the panel specified or null if it's not in this region.
49501      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49502      * @return {Roo.ContentPanel}
49503      */
49504     getPanel : function(id){
49505         if(typeof id == "object"){ // must be panel obj
49506             return id;
49507         }
49508         return this.panels.get(id);
49509     },
49510     
49511     /**
49512      * Returns this regions position (north/south/east/west/center).
49513      * @return {String} 
49514      */
49515     getPosition: function(){
49516         return this.position;    
49517     }
49518 });/*
49519  * Based on:
49520  * Ext JS Library 1.1.1
49521  * Copyright(c) 2006-2007, Ext JS, LLC.
49522  *
49523  * Originally Released Under LGPL - original licence link has changed is not relivant.
49524  *
49525  * Fork - LGPL
49526  * <script type="text/javascript">
49527  */
49528  
49529 /**
49530  * @class Roo.LayoutRegion
49531  * @extends Roo.BasicLayoutRegion
49532  * This class represents a region in a layout manager.
49533  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49534  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49535  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49536  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49537  * @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})
49538  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49539  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49540  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49541  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49542  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49543  * @cfg {String}    title           The title for the region (overrides panel titles)
49544  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49545  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49546  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49547  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49548  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49549  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49550  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49551  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49552  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49553  * @cfg {Boolean}   showPin         True to show a pin button
49554  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49555  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49556  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49557  * @cfg {Number}    width           For East/West panels
49558  * @cfg {Number}    height          For North/South panels
49559  * @cfg {Boolean}   split           To show the splitter
49560  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49561  */
49562 Roo.LayoutRegion = function(mgr, config, pos){
49563     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49564     var dh = Roo.DomHelper;
49565     /** This region's container element 
49566     * @type Roo.Element */
49567     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49568     /** This region's title element 
49569     * @type Roo.Element */
49570
49571     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49572         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49573         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49574     ]}, true);
49575     this.titleEl.enableDisplayMode();
49576     /** This region's title text element 
49577     * @type HTMLElement */
49578     this.titleTextEl = this.titleEl.dom.firstChild;
49579     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49580     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49581     this.closeBtn.enableDisplayMode();
49582     this.closeBtn.on("click", this.closeClicked, this);
49583     this.closeBtn.hide();
49584
49585     this.createBody(config);
49586     this.visible = true;
49587     this.collapsed = false;
49588
49589     if(config.hideWhenEmpty){
49590         this.hide();
49591         this.on("paneladded", this.validateVisibility, this);
49592         this.on("panelremoved", this.validateVisibility, this);
49593     }
49594     this.applyConfig(config);
49595 };
49596
49597 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49598
49599     createBody : function(){
49600         /** This region's body element 
49601         * @type Roo.Element */
49602         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49603     },
49604
49605     applyConfig : function(c){
49606         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49607             var dh = Roo.DomHelper;
49608             if(c.titlebar !== false){
49609                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49610                 this.collapseBtn.on("click", this.collapse, this);
49611                 this.collapseBtn.enableDisplayMode();
49612
49613                 if(c.showPin === true || this.showPin){
49614                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49615                     this.stickBtn.enableDisplayMode();
49616                     this.stickBtn.on("click", this.expand, this);
49617                     this.stickBtn.hide();
49618                 }
49619             }
49620             /** This region's collapsed element
49621             * @type Roo.Element */
49622             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49623                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49624             ]}, true);
49625             if(c.floatable !== false){
49626                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49627                this.collapsedEl.on("click", this.collapseClick, this);
49628             }
49629
49630             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49631                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49632                    id: "message", unselectable: "on", style:{"float":"left"}});
49633                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49634              }
49635             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49636             this.expandBtn.on("click", this.expand, this);
49637         }
49638         if(this.collapseBtn){
49639             this.collapseBtn.setVisible(c.collapsible == true);
49640         }
49641         this.cmargins = c.cmargins || this.cmargins ||
49642                          (this.position == "west" || this.position == "east" ?
49643                              {top: 0, left: 2, right:2, bottom: 0} :
49644                              {top: 2, left: 0, right:0, bottom: 2});
49645         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49646         this.bottomTabs = c.tabPosition != "top";
49647         this.autoScroll = c.autoScroll || false;
49648         if(this.autoScroll){
49649             this.bodyEl.setStyle("overflow", "auto");
49650         }else{
49651             this.bodyEl.setStyle("overflow", "hidden");
49652         }
49653         //if(c.titlebar !== false){
49654             if((!c.titlebar && !c.title) || c.titlebar === false){
49655                 this.titleEl.hide();
49656             }else{
49657                 this.titleEl.show();
49658                 if(c.title){
49659                     this.titleTextEl.innerHTML = c.title;
49660                 }
49661             }
49662         //}
49663         this.duration = c.duration || .30;
49664         this.slideDuration = c.slideDuration || .45;
49665         this.config = c;
49666         if(c.collapsed){
49667             this.collapse(true);
49668         }
49669         if(c.hidden){
49670             this.hide();
49671         }
49672     },
49673     /**
49674      * Returns true if this region is currently visible.
49675      * @return {Boolean}
49676      */
49677     isVisible : function(){
49678         return this.visible;
49679     },
49680
49681     /**
49682      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49683      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49684      */
49685     setCollapsedTitle : function(title){
49686         title = title || "&#160;";
49687         if(this.collapsedTitleTextEl){
49688             this.collapsedTitleTextEl.innerHTML = title;
49689         }
49690     },
49691
49692     getBox : function(){
49693         var b;
49694         if(!this.collapsed){
49695             b = this.el.getBox(false, true);
49696         }else{
49697             b = this.collapsedEl.getBox(false, true);
49698         }
49699         return b;
49700     },
49701
49702     getMargins : function(){
49703         return this.collapsed ? this.cmargins : this.margins;
49704     },
49705
49706     highlight : function(){
49707         this.el.addClass("x-layout-panel-dragover");
49708     },
49709
49710     unhighlight : function(){
49711         this.el.removeClass("x-layout-panel-dragover");
49712     },
49713
49714     updateBox : function(box){
49715         this.box = box;
49716         if(!this.collapsed){
49717             this.el.dom.style.left = box.x + "px";
49718             this.el.dom.style.top = box.y + "px";
49719             this.updateBody(box.width, box.height);
49720         }else{
49721             this.collapsedEl.dom.style.left = box.x + "px";
49722             this.collapsedEl.dom.style.top = box.y + "px";
49723             this.collapsedEl.setSize(box.width, box.height);
49724         }
49725         if(this.tabs){
49726             this.tabs.autoSizeTabs();
49727         }
49728     },
49729
49730     updateBody : function(w, h){
49731         if(w !== null){
49732             this.el.setWidth(w);
49733             w -= this.el.getBorderWidth("rl");
49734             if(this.config.adjustments){
49735                 w += this.config.adjustments[0];
49736             }
49737         }
49738         if(h !== null){
49739             this.el.setHeight(h);
49740             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49741             h -= this.el.getBorderWidth("tb");
49742             if(this.config.adjustments){
49743                 h += this.config.adjustments[1];
49744             }
49745             this.bodyEl.setHeight(h);
49746             if(this.tabs){
49747                 h = this.tabs.syncHeight(h);
49748             }
49749         }
49750         if(this.panelSize){
49751             w = w !== null ? w : this.panelSize.width;
49752             h = h !== null ? h : this.panelSize.height;
49753         }
49754         if(this.activePanel){
49755             var el = this.activePanel.getEl();
49756             w = w !== null ? w : el.getWidth();
49757             h = h !== null ? h : el.getHeight();
49758             this.panelSize = {width: w, height: h};
49759             this.activePanel.setSize(w, h);
49760         }
49761         if(Roo.isIE && this.tabs){
49762             this.tabs.el.repaint();
49763         }
49764     },
49765
49766     /**
49767      * Returns the container element for this region.
49768      * @return {Roo.Element}
49769      */
49770     getEl : function(){
49771         return this.el;
49772     },
49773
49774     /**
49775      * Hides this region.
49776      */
49777     hide : function(){
49778         if(!this.collapsed){
49779             this.el.dom.style.left = "-2000px";
49780             this.el.hide();
49781         }else{
49782             this.collapsedEl.dom.style.left = "-2000px";
49783             this.collapsedEl.hide();
49784         }
49785         this.visible = false;
49786         this.fireEvent("visibilitychange", this, false);
49787     },
49788
49789     /**
49790      * Shows this region if it was previously hidden.
49791      */
49792     show : function(){
49793         if(!this.collapsed){
49794             this.el.show();
49795         }else{
49796             this.collapsedEl.show();
49797         }
49798         this.visible = true;
49799         this.fireEvent("visibilitychange", this, true);
49800     },
49801
49802     closeClicked : function(){
49803         if(this.activePanel){
49804             this.remove(this.activePanel);
49805         }
49806     },
49807
49808     collapseClick : function(e){
49809         if(this.isSlid){
49810            e.stopPropagation();
49811            this.slideIn();
49812         }else{
49813            e.stopPropagation();
49814            this.slideOut();
49815         }
49816     },
49817
49818     /**
49819      * Collapses this region.
49820      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49821      */
49822     collapse : function(skipAnim){
49823         if(this.collapsed) return;
49824         this.collapsed = true;
49825         if(this.split){
49826             this.split.el.hide();
49827         }
49828         if(this.config.animate && skipAnim !== true){
49829             this.fireEvent("invalidated", this);
49830             this.animateCollapse();
49831         }else{
49832             this.el.setLocation(-20000,-20000);
49833             this.el.hide();
49834             this.collapsedEl.show();
49835             this.fireEvent("collapsed", this);
49836             this.fireEvent("invalidated", this);
49837         }
49838     },
49839
49840     animateCollapse : function(){
49841         // overridden
49842     },
49843
49844     /**
49845      * Expands this region if it was previously collapsed.
49846      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49847      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49848      */
49849     expand : function(e, skipAnim){
49850         if(e) e.stopPropagation();
49851         if(!this.collapsed || this.el.hasActiveFx()) return;
49852         if(this.isSlid){
49853             this.afterSlideIn();
49854             skipAnim = true;
49855         }
49856         this.collapsed = false;
49857         if(this.config.animate && skipAnim !== true){
49858             this.animateExpand();
49859         }else{
49860             this.el.show();
49861             if(this.split){
49862                 this.split.el.show();
49863             }
49864             this.collapsedEl.setLocation(-2000,-2000);
49865             this.collapsedEl.hide();
49866             this.fireEvent("invalidated", this);
49867             this.fireEvent("expanded", this);
49868         }
49869     },
49870
49871     animateExpand : function(){
49872         // overridden
49873     },
49874
49875     initTabs : function()
49876     {
49877         this.bodyEl.setStyle("overflow", "hidden");
49878         var ts = new Roo.TabPanel(
49879                 this.bodyEl.dom,
49880                 {
49881                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49882                     disableTooltips: this.config.disableTabTips,
49883                     toolbar : this.config.toolbar
49884                 }
49885         );
49886         if(this.config.hideTabs){
49887             ts.stripWrap.setDisplayed(false);
49888         }
49889         this.tabs = ts;
49890         ts.resizeTabs = this.config.resizeTabs === true;
49891         ts.minTabWidth = this.config.minTabWidth || 40;
49892         ts.maxTabWidth = this.config.maxTabWidth || 250;
49893         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49894         ts.monitorResize = false;
49895         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49896         ts.bodyEl.addClass('x-layout-tabs-body');
49897         this.panels.each(this.initPanelAsTab, this);
49898     },
49899
49900     initPanelAsTab : function(panel){
49901         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49902                     this.config.closeOnTab && panel.isClosable());
49903         if(panel.tabTip !== undefined){
49904             ti.setTooltip(panel.tabTip);
49905         }
49906         ti.on("activate", function(){
49907               this.setActivePanel(panel);
49908         }, this);
49909         if(this.config.closeOnTab){
49910             ti.on("beforeclose", function(t, e){
49911                 e.cancel = true;
49912                 this.remove(panel);
49913             }, this);
49914         }
49915         return ti;
49916     },
49917
49918     updatePanelTitle : function(panel, title){
49919         if(this.activePanel == panel){
49920             this.updateTitle(title);
49921         }
49922         if(this.tabs){
49923             var ti = this.tabs.getTab(panel.getEl().id);
49924             ti.setText(title);
49925             if(panel.tabTip !== undefined){
49926                 ti.setTooltip(panel.tabTip);
49927             }
49928         }
49929     },
49930
49931     updateTitle : function(title){
49932         if(this.titleTextEl && !this.config.title){
49933             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49934         }
49935     },
49936
49937     setActivePanel : function(panel){
49938         panel = this.getPanel(panel);
49939         if(this.activePanel && this.activePanel != panel){
49940             this.activePanel.setActiveState(false);
49941         }
49942         this.activePanel = panel;
49943         panel.setActiveState(true);
49944         if(this.panelSize){
49945             panel.setSize(this.panelSize.width, this.panelSize.height);
49946         }
49947         if(this.closeBtn){
49948             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49949         }
49950         this.updateTitle(panel.getTitle());
49951         if(this.tabs){
49952             this.fireEvent("invalidated", this);
49953         }
49954         this.fireEvent("panelactivated", this, panel);
49955     },
49956
49957     /**
49958      * Shows the specified panel.
49959      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49960      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49961      */
49962     showPanel : function(panel){
49963         if(panel = this.getPanel(panel)){
49964             if(this.tabs){
49965                 var tab = this.tabs.getTab(panel.getEl().id);
49966                 if(tab.isHidden()){
49967                     this.tabs.unhideTab(tab.id);
49968                 }
49969                 tab.activate();
49970             }else{
49971                 this.setActivePanel(panel);
49972             }
49973         }
49974         return panel;
49975     },
49976
49977     /**
49978      * Get the active panel for this region.
49979      * @return {Roo.ContentPanel} The active panel or null
49980      */
49981     getActivePanel : function(){
49982         return this.activePanel;
49983     },
49984
49985     validateVisibility : function(){
49986         if(this.panels.getCount() < 1){
49987             this.updateTitle("&#160;");
49988             this.closeBtn.hide();
49989             this.hide();
49990         }else{
49991             if(!this.isVisible()){
49992                 this.show();
49993             }
49994         }
49995     },
49996
49997     /**
49998      * Adds the passed ContentPanel(s) to this region.
49999      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50000      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50001      */
50002     add : function(panel){
50003         if(arguments.length > 1){
50004             for(var i = 0, len = arguments.length; i < len; i++) {
50005                 this.add(arguments[i]);
50006             }
50007             return null;
50008         }
50009         if(this.hasPanel(panel)){
50010             this.showPanel(panel);
50011             return panel;
50012         }
50013         panel.setRegion(this);
50014         this.panels.add(panel);
50015         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50016             this.bodyEl.dom.appendChild(panel.getEl().dom);
50017             if(panel.background !== true){
50018                 this.setActivePanel(panel);
50019             }
50020             this.fireEvent("paneladded", this, panel);
50021             return panel;
50022         }
50023         if(!this.tabs){
50024             this.initTabs();
50025         }else{
50026             this.initPanelAsTab(panel);
50027         }
50028         if(panel.background !== true){
50029             this.tabs.activate(panel.getEl().id);
50030         }
50031         this.fireEvent("paneladded", this, panel);
50032         return panel;
50033     },
50034
50035     /**
50036      * Hides the tab for the specified panel.
50037      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50038      */
50039     hidePanel : function(panel){
50040         if(this.tabs && (panel = this.getPanel(panel))){
50041             this.tabs.hideTab(panel.getEl().id);
50042         }
50043     },
50044
50045     /**
50046      * Unhides the tab for a previously hidden panel.
50047      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50048      */
50049     unhidePanel : function(panel){
50050         if(this.tabs && (panel = this.getPanel(panel))){
50051             this.tabs.unhideTab(panel.getEl().id);
50052         }
50053     },
50054
50055     clearPanels : function(){
50056         while(this.panels.getCount() > 0){
50057              this.remove(this.panels.first());
50058         }
50059     },
50060
50061     /**
50062      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50063      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50064      * @param {Boolean} preservePanel Overrides the config preservePanel option
50065      * @return {Roo.ContentPanel} The panel that was removed
50066      */
50067     remove : function(panel, preservePanel){
50068         panel = this.getPanel(panel);
50069         if(!panel){
50070             return null;
50071         }
50072         var e = {};
50073         this.fireEvent("beforeremove", this, panel, e);
50074         if(e.cancel === true){
50075             return null;
50076         }
50077         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50078         var panelId = panel.getId();
50079         this.panels.removeKey(panelId);
50080         if(preservePanel){
50081             document.body.appendChild(panel.getEl().dom);
50082         }
50083         if(this.tabs){
50084             this.tabs.removeTab(panel.getEl().id);
50085         }else if (!preservePanel){
50086             this.bodyEl.dom.removeChild(panel.getEl().dom);
50087         }
50088         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50089             var p = this.panels.first();
50090             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50091             tempEl.appendChild(p.getEl().dom);
50092             this.bodyEl.update("");
50093             this.bodyEl.dom.appendChild(p.getEl().dom);
50094             tempEl = null;
50095             this.updateTitle(p.getTitle());
50096             this.tabs = null;
50097             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50098             this.setActivePanel(p);
50099         }
50100         panel.setRegion(null);
50101         if(this.activePanel == panel){
50102             this.activePanel = null;
50103         }
50104         if(this.config.autoDestroy !== false && preservePanel !== true){
50105             try{panel.destroy();}catch(e){}
50106         }
50107         this.fireEvent("panelremoved", this, panel);
50108         return panel;
50109     },
50110
50111     /**
50112      * Returns the TabPanel component used by this region
50113      * @return {Roo.TabPanel}
50114      */
50115     getTabs : function(){
50116         return this.tabs;
50117     },
50118
50119     createTool : function(parentEl, className){
50120         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50121             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50122         btn.addClassOnOver("x-layout-tools-button-over");
50123         return btn;
50124     }
50125 });/*
50126  * Based on:
50127  * Ext JS Library 1.1.1
50128  * Copyright(c) 2006-2007, Ext JS, LLC.
50129  *
50130  * Originally Released Under LGPL - original licence link has changed is not relivant.
50131  *
50132  * Fork - LGPL
50133  * <script type="text/javascript">
50134  */
50135  
50136
50137
50138 /**
50139  * @class Roo.SplitLayoutRegion
50140  * @extends Roo.LayoutRegion
50141  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50142  */
50143 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50144     this.cursor = cursor;
50145     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50146 };
50147
50148 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50149     splitTip : "Drag to resize.",
50150     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50151     useSplitTips : false,
50152
50153     applyConfig : function(config){
50154         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50155         if(config.split){
50156             if(!this.split){
50157                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50158                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50159                 /** The SplitBar for this region 
50160                 * @type Roo.SplitBar */
50161                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50162                 this.split.on("moved", this.onSplitMove, this);
50163                 this.split.useShim = config.useShim === true;
50164                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50165                 if(this.useSplitTips){
50166                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50167                 }
50168                 if(config.collapsible){
50169                     this.split.el.on("dblclick", this.collapse,  this);
50170                 }
50171             }
50172             if(typeof config.minSize != "undefined"){
50173                 this.split.minSize = config.minSize;
50174             }
50175             if(typeof config.maxSize != "undefined"){
50176                 this.split.maxSize = config.maxSize;
50177             }
50178             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50179                 this.hideSplitter();
50180             }
50181         }
50182     },
50183
50184     getHMaxSize : function(){
50185          var cmax = this.config.maxSize || 10000;
50186          var center = this.mgr.getRegion("center");
50187          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50188     },
50189
50190     getVMaxSize : function(){
50191          var cmax = this.config.maxSize || 10000;
50192          var center = this.mgr.getRegion("center");
50193          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50194     },
50195
50196     onSplitMove : function(split, newSize){
50197         this.fireEvent("resized", this, newSize);
50198     },
50199     
50200     /** 
50201      * Returns the {@link Roo.SplitBar} for this region.
50202      * @return {Roo.SplitBar}
50203      */
50204     getSplitBar : function(){
50205         return this.split;
50206     },
50207     
50208     hide : function(){
50209         this.hideSplitter();
50210         Roo.SplitLayoutRegion.superclass.hide.call(this);
50211     },
50212
50213     hideSplitter : function(){
50214         if(this.split){
50215             this.split.el.setLocation(-2000,-2000);
50216             this.split.el.hide();
50217         }
50218     },
50219
50220     show : function(){
50221         if(this.split){
50222             this.split.el.show();
50223         }
50224         Roo.SplitLayoutRegion.superclass.show.call(this);
50225     },
50226     
50227     beforeSlide: function(){
50228         if(Roo.isGecko){// firefox overflow auto bug workaround
50229             this.bodyEl.clip();
50230             if(this.tabs) this.tabs.bodyEl.clip();
50231             if(this.activePanel){
50232                 this.activePanel.getEl().clip();
50233                 
50234                 if(this.activePanel.beforeSlide){
50235                     this.activePanel.beforeSlide();
50236                 }
50237             }
50238         }
50239     },
50240     
50241     afterSlide : function(){
50242         if(Roo.isGecko){// firefox overflow auto bug workaround
50243             this.bodyEl.unclip();
50244             if(this.tabs) this.tabs.bodyEl.unclip();
50245             if(this.activePanel){
50246                 this.activePanel.getEl().unclip();
50247                 if(this.activePanel.afterSlide){
50248                     this.activePanel.afterSlide();
50249                 }
50250             }
50251         }
50252     },
50253
50254     initAutoHide : function(){
50255         if(this.autoHide !== false){
50256             if(!this.autoHideHd){
50257                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50258                 this.autoHideHd = {
50259                     "mouseout": function(e){
50260                         if(!e.within(this.el, true)){
50261                             st.delay(500);
50262                         }
50263                     },
50264                     "mouseover" : function(e){
50265                         st.cancel();
50266                     },
50267                     scope : this
50268                 };
50269             }
50270             this.el.on(this.autoHideHd);
50271         }
50272     },
50273
50274     clearAutoHide : function(){
50275         if(this.autoHide !== false){
50276             this.el.un("mouseout", this.autoHideHd.mouseout);
50277             this.el.un("mouseover", this.autoHideHd.mouseover);
50278         }
50279     },
50280
50281     clearMonitor : function(){
50282         Roo.get(document).un("click", this.slideInIf, this);
50283     },
50284
50285     // these names are backwards but not changed for compat
50286     slideOut : function(){
50287         if(this.isSlid || this.el.hasActiveFx()){
50288             return;
50289         }
50290         this.isSlid = true;
50291         if(this.collapseBtn){
50292             this.collapseBtn.hide();
50293         }
50294         this.closeBtnState = this.closeBtn.getStyle('display');
50295         this.closeBtn.hide();
50296         if(this.stickBtn){
50297             this.stickBtn.show();
50298         }
50299         this.el.show();
50300         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50301         this.beforeSlide();
50302         this.el.setStyle("z-index", 10001);
50303         this.el.slideIn(this.getSlideAnchor(), {
50304             callback: function(){
50305                 this.afterSlide();
50306                 this.initAutoHide();
50307                 Roo.get(document).on("click", this.slideInIf, this);
50308                 this.fireEvent("slideshow", this);
50309             },
50310             scope: this,
50311             block: true
50312         });
50313     },
50314
50315     afterSlideIn : function(){
50316         this.clearAutoHide();
50317         this.isSlid = false;
50318         this.clearMonitor();
50319         this.el.setStyle("z-index", "");
50320         if(this.collapseBtn){
50321             this.collapseBtn.show();
50322         }
50323         this.closeBtn.setStyle('display', this.closeBtnState);
50324         if(this.stickBtn){
50325             this.stickBtn.hide();
50326         }
50327         this.fireEvent("slidehide", this);
50328     },
50329
50330     slideIn : function(cb){
50331         if(!this.isSlid || this.el.hasActiveFx()){
50332             Roo.callback(cb);
50333             return;
50334         }
50335         this.isSlid = false;
50336         this.beforeSlide();
50337         this.el.slideOut(this.getSlideAnchor(), {
50338             callback: function(){
50339                 this.el.setLeftTop(-10000, -10000);
50340                 this.afterSlide();
50341                 this.afterSlideIn();
50342                 Roo.callback(cb);
50343             },
50344             scope: this,
50345             block: true
50346         });
50347     },
50348     
50349     slideInIf : function(e){
50350         if(!e.within(this.el)){
50351             this.slideIn();
50352         }
50353     },
50354
50355     animateCollapse : function(){
50356         this.beforeSlide();
50357         this.el.setStyle("z-index", 20000);
50358         var anchor = this.getSlideAnchor();
50359         this.el.slideOut(anchor, {
50360             callback : function(){
50361                 this.el.setStyle("z-index", "");
50362                 this.collapsedEl.slideIn(anchor, {duration:.3});
50363                 this.afterSlide();
50364                 this.el.setLocation(-10000,-10000);
50365                 this.el.hide();
50366                 this.fireEvent("collapsed", this);
50367             },
50368             scope: this,
50369             block: true
50370         });
50371     },
50372
50373     animateExpand : function(){
50374         this.beforeSlide();
50375         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50376         this.el.setStyle("z-index", 20000);
50377         this.collapsedEl.hide({
50378             duration:.1
50379         });
50380         this.el.slideIn(this.getSlideAnchor(), {
50381             callback : function(){
50382                 this.el.setStyle("z-index", "");
50383                 this.afterSlide();
50384                 if(this.split){
50385                     this.split.el.show();
50386                 }
50387                 this.fireEvent("invalidated", this);
50388                 this.fireEvent("expanded", this);
50389             },
50390             scope: this,
50391             block: true
50392         });
50393     },
50394
50395     anchors : {
50396         "west" : "left",
50397         "east" : "right",
50398         "north" : "top",
50399         "south" : "bottom"
50400     },
50401
50402     sanchors : {
50403         "west" : "l",
50404         "east" : "r",
50405         "north" : "t",
50406         "south" : "b"
50407     },
50408
50409     canchors : {
50410         "west" : "tl-tr",
50411         "east" : "tr-tl",
50412         "north" : "tl-bl",
50413         "south" : "bl-tl"
50414     },
50415
50416     getAnchor : function(){
50417         return this.anchors[this.position];
50418     },
50419
50420     getCollapseAnchor : function(){
50421         return this.canchors[this.position];
50422     },
50423
50424     getSlideAnchor : function(){
50425         return this.sanchors[this.position];
50426     },
50427
50428     getAlignAdj : function(){
50429         var cm = this.cmargins;
50430         switch(this.position){
50431             case "west":
50432                 return [0, 0];
50433             break;
50434             case "east":
50435                 return [0, 0];
50436             break;
50437             case "north":
50438                 return [0, 0];
50439             break;
50440             case "south":
50441                 return [0, 0];
50442             break;
50443         }
50444     },
50445
50446     getExpandAdj : function(){
50447         var c = this.collapsedEl, cm = this.cmargins;
50448         switch(this.position){
50449             case "west":
50450                 return [-(cm.right+c.getWidth()+cm.left), 0];
50451             break;
50452             case "east":
50453                 return [cm.right+c.getWidth()+cm.left, 0];
50454             break;
50455             case "north":
50456                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50457             break;
50458             case "south":
50459                 return [0, cm.top+cm.bottom+c.getHeight()];
50460             break;
50461         }
50462     }
50463 });/*
50464  * Based on:
50465  * Ext JS Library 1.1.1
50466  * Copyright(c) 2006-2007, Ext JS, LLC.
50467  *
50468  * Originally Released Under LGPL - original licence link has changed is not relivant.
50469  *
50470  * Fork - LGPL
50471  * <script type="text/javascript">
50472  */
50473 /*
50474  * These classes are private internal classes
50475  */
50476 Roo.CenterLayoutRegion = function(mgr, config){
50477     Roo.LayoutRegion.call(this, mgr, config, "center");
50478     this.visible = true;
50479     this.minWidth = config.minWidth || 20;
50480     this.minHeight = config.minHeight || 20;
50481 };
50482
50483 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50484     hide : function(){
50485         // center panel can't be hidden
50486     },
50487     
50488     show : function(){
50489         // center panel can't be hidden
50490     },
50491     
50492     getMinWidth: function(){
50493         return this.minWidth;
50494     },
50495     
50496     getMinHeight: function(){
50497         return this.minHeight;
50498     }
50499 });
50500
50501
50502 Roo.NorthLayoutRegion = function(mgr, config){
50503     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50504     if(this.split){
50505         this.split.placement = Roo.SplitBar.TOP;
50506         this.split.orientation = Roo.SplitBar.VERTICAL;
50507         this.split.el.addClass("x-layout-split-v");
50508     }
50509     var size = config.initialSize || config.height;
50510     if(typeof size != "undefined"){
50511         this.el.setHeight(size);
50512     }
50513 };
50514 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50515     orientation: Roo.SplitBar.VERTICAL,
50516     getBox : function(){
50517         if(this.collapsed){
50518             return this.collapsedEl.getBox();
50519         }
50520         var box = this.el.getBox();
50521         if(this.split){
50522             box.height += this.split.el.getHeight();
50523         }
50524         return box;
50525     },
50526     
50527     updateBox : function(box){
50528         if(this.split && !this.collapsed){
50529             box.height -= this.split.el.getHeight();
50530             this.split.el.setLeft(box.x);
50531             this.split.el.setTop(box.y+box.height);
50532             this.split.el.setWidth(box.width);
50533         }
50534         if(this.collapsed){
50535             this.updateBody(box.width, null);
50536         }
50537         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50538     }
50539 });
50540
50541 Roo.SouthLayoutRegion = function(mgr, config){
50542     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50543     if(this.split){
50544         this.split.placement = Roo.SplitBar.BOTTOM;
50545         this.split.orientation = Roo.SplitBar.VERTICAL;
50546         this.split.el.addClass("x-layout-split-v");
50547     }
50548     var size = config.initialSize || config.height;
50549     if(typeof size != "undefined"){
50550         this.el.setHeight(size);
50551     }
50552 };
50553 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50554     orientation: Roo.SplitBar.VERTICAL,
50555     getBox : function(){
50556         if(this.collapsed){
50557             return this.collapsedEl.getBox();
50558         }
50559         var box = this.el.getBox();
50560         if(this.split){
50561             var sh = this.split.el.getHeight();
50562             box.height += sh;
50563             box.y -= sh;
50564         }
50565         return box;
50566     },
50567     
50568     updateBox : function(box){
50569         if(this.split && !this.collapsed){
50570             var sh = this.split.el.getHeight();
50571             box.height -= sh;
50572             box.y += sh;
50573             this.split.el.setLeft(box.x);
50574             this.split.el.setTop(box.y-sh);
50575             this.split.el.setWidth(box.width);
50576         }
50577         if(this.collapsed){
50578             this.updateBody(box.width, null);
50579         }
50580         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50581     }
50582 });
50583
50584 Roo.EastLayoutRegion = function(mgr, config){
50585     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50586     if(this.split){
50587         this.split.placement = Roo.SplitBar.RIGHT;
50588         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50589         this.split.el.addClass("x-layout-split-h");
50590     }
50591     var size = config.initialSize || config.width;
50592     if(typeof size != "undefined"){
50593         this.el.setWidth(size);
50594     }
50595 };
50596 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50597     orientation: Roo.SplitBar.HORIZONTAL,
50598     getBox : function(){
50599         if(this.collapsed){
50600             return this.collapsedEl.getBox();
50601         }
50602         var box = this.el.getBox();
50603         if(this.split){
50604             var sw = this.split.el.getWidth();
50605             box.width += sw;
50606             box.x -= sw;
50607         }
50608         return box;
50609     },
50610
50611     updateBox : function(box){
50612         if(this.split && !this.collapsed){
50613             var sw = this.split.el.getWidth();
50614             box.width -= sw;
50615             this.split.el.setLeft(box.x);
50616             this.split.el.setTop(box.y);
50617             this.split.el.setHeight(box.height);
50618             box.x += sw;
50619         }
50620         if(this.collapsed){
50621             this.updateBody(null, box.height);
50622         }
50623         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50624     }
50625 });
50626
50627 Roo.WestLayoutRegion = function(mgr, config){
50628     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50629     if(this.split){
50630         this.split.placement = Roo.SplitBar.LEFT;
50631         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50632         this.split.el.addClass("x-layout-split-h");
50633     }
50634     var size = config.initialSize || config.width;
50635     if(typeof size != "undefined"){
50636         this.el.setWidth(size);
50637     }
50638 };
50639 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50640     orientation: Roo.SplitBar.HORIZONTAL,
50641     getBox : function(){
50642         if(this.collapsed){
50643             return this.collapsedEl.getBox();
50644         }
50645         var box = this.el.getBox();
50646         if(this.split){
50647             box.width += this.split.el.getWidth();
50648         }
50649         return box;
50650     },
50651     
50652     updateBox : function(box){
50653         if(this.split && !this.collapsed){
50654             var sw = this.split.el.getWidth();
50655             box.width -= sw;
50656             this.split.el.setLeft(box.x+box.width);
50657             this.split.el.setTop(box.y);
50658             this.split.el.setHeight(box.height);
50659         }
50660         if(this.collapsed){
50661             this.updateBody(null, box.height);
50662         }
50663         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50664     }
50665 });
50666 /*
50667  * Based on:
50668  * Ext JS Library 1.1.1
50669  * Copyright(c) 2006-2007, Ext JS, LLC.
50670  *
50671  * Originally Released Under LGPL - original licence link has changed is not relivant.
50672  *
50673  * Fork - LGPL
50674  * <script type="text/javascript">
50675  */
50676  
50677  
50678 /*
50679  * Private internal class for reading and applying state
50680  */
50681 Roo.LayoutStateManager = function(layout){
50682      // default empty state
50683      this.state = {
50684         north: {},
50685         south: {},
50686         east: {},
50687         west: {}       
50688     };
50689 };
50690
50691 Roo.LayoutStateManager.prototype = {
50692     init : function(layout, provider){
50693         this.provider = provider;
50694         var state = provider.get(layout.id+"-layout-state");
50695         if(state){
50696             var wasUpdating = layout.isUpdating();
50697             if(!wasUpdating){
50698                 layout.beginUpdate();
50699             }
50700             for(var key in state){
50701                 if(typeof state[key] != "function"){
50702                     var rstate = state[key];
50703                     var r = layout.getRegion(key);
50704                     if(r && rstate){
50705                         if(rstate.size){
50706                             r.resizeTo(rstate.size);
50707                         }
50708                         if(rstate.collapsed == true){
50709                             r.collapse(true);
50710                         }else{
50711                             r.expand(null, true);
50712                         }
50713                     }
50714                 }
50715             }
50716             if(!wasUpdating){
50717                 layout.endUpdate();
50718             }
50719             this.state = state; 
50720         }
50721         this.layout = layout;
50722         layout.on("regionresized", this.onRegionResized, this);
50723         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50724         layout.on("regionexpanded", this.onRegionExpanded, this);
50725     },
50726     
50727     storeState : function(){
50728         this.provider.set(this.layout.id+"-layout-state", this.state);
50729     },
50730     
50731     onRegionResized : function(region, newSize){
50732         this.state[region.getPosition()].size = newSize;
50733         this.storeState();
50734     },
50735     
50736     onRegionCollapsed : function(region){
50737         this.state[region.getPosition()].collapsed = true;
50738         this.storeState();
50739     },
50740     
50741     onRegionExpanded : function(region){
50742         this.state[region.getPosition()].collapsed = false;
50743         this.storeState();
50744     }
50745 };/*
50746  * Based on:
50747  * Ext JS Library 1.1.1
50748  * Copyright(c) 2006-2007, Ext JS, LLC.
50749  *
50750  * Originally Released Under LGPL - original licence link has changed is not relivant.
50751  *
50752  * Fork - LGPL
50753  * <script type="text/javascript">
50754  */
50755 /**
50756  * @class Roo.ContentPanel
50757  * @extends Roo.util.Observable
50758  * A basic ContentPanel element.
50759  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50760  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50761  * @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
50762  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50763  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50764  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50765  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50766  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50767  * @cfg {String} title          The title for this panel
50768  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50769  * @cfg {String} url            Calls {@link #setUrl} with this value
50770  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50771  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50772  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50773  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50774
50775  * @constructor
50776  * Create a new ContentPanel.
50777  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50778  * @param {String/Object} config A string to set only the title or a config object
50779  * @param {String} content (optional) Set the HTML content for this panel
50780  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50781  */
50782 Roo.ContentPanel = function(el, config, content){
50783     
50784      
50785     /*
50786     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50787         config = el;
50788         el = Roo.id();
50789     }
50790     if (config && config.parentLayout) { 
50791         el = config.parentLayout.el.createChild(); 
50792     }
50793     */
50794     if(el.autoCreate){ // xtype is available if this is called from factory
50795         config = el;
50796         el = Roo.id();
50797     }
50798     this.el = Roo.get(el);
50799     if(!this.el && config && config.autoCreate){
50800         if(typeof config.autoCreate == "object"){
50801             if(!config.autoCreate.id){
50802                 config.autoCreate.id = config.id||el;
50803             }
50804             this.el = Roo.DomHelper.append(document.body,
50805                         config.autoCreate, true);
50806         }else{
50807             this.el = Roo.DomHelper.append(document.body,
50808                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50809         }
50810     }
50811     this.closable = false;
50812     this.loaded = false;
50813     this.active = false;
50814     if(typeof config == "string"){
50815         this.title = config;
50816     }else{
50817         Roo.apply(this, config);
50818     }
50819     
50820     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50821         this.wrapEl = this.el.wrap();
50822         this.toolbar.container = this.el.insertSibling(false, 'before');
50823         this.toolbar = new Roo.Toolbar(this.toolbar);
50824     }
50825     
50826     // xtype created footer. - not sure if will work as we normally have to render first..
50827     if (this.footer && !this.footer.el && this.footer.xtype) {
50828         if (!this.wrapEl) {
50829             this.wrapEl = this.el.wrap();
50830         }
50831     
50832         this.footer.container = this.wrapEl.createChild();
50833          
50834         this.footer = Roo.factory(this.footer, Roo);
50835         
50836     }
50837     
50838     if(this.resizeEl){
50839         this.resizeEl = Roo.get(this.resizeEl, true);
50840     }else{
50841         this.resizeEl = this.el;
50842     }
50843     // handle view.xtype
50844     
50845  
50846     
50847     
50848     this.addEvents({
50849         /**
50850          * @event activate
50851          * Fires when this panel is activated. 
50852          * @param {Roo.ContentPanel} this
50853          */
50854         "activate" : true,
50855         /**
50856          * @event deactivate
50857          * Fires when this panel is activated. 
50858          * @param {Roo.ContentPanel} this
50859          */
50860         "deactivate" : true,
50861
50862         /**
50863          * @event resize
50864          * Fires when this panel is resized if fitToFrame is true.
50865          * @param {Roo.ContentPanel} this
50866          * @param {Number} width The width after any component adjustments
50867          * @param {Number} height The height after any component adjustments
50868          */
50869         "resize" : true,
50870         
50871          /**
50872          * @event render
50873          * Fires when this tab is created
50874          * @param {Roo.ContentPanel} this
50875          */
50876         "render" : true
50877         
50878         
50879         
50880     });
50881     
50882
50883     
50884     
50885     if(this.autoScroll){
50886         this.resizeEl.setStyle("overflow", "auto");
50887     } else {
50888         // fix randome scrolling
50889         this.el.on('scroll', function() {
50890             Roo.log('fix random scolling');
50891             this.scrollTo('top',0); 
50892         });
50893     }
50894     content = content || this.content;
50895     if(content){
50896         this.setContent(content);
50897     }
50898     if(config && config.url){
50899         this.setUrl(this.url, this.params, this.loadOnce);
50900     }
50901     
50902     
50903     
50904     Roo.ContentPanel.superclass.constructor.call(this);
50905     
50906     if (this.view && typeof(this.view.xtype) != 'undefined') {
50907         this.view.el = this.el.appendChild(document.createElement("div"));
50908         this.view = Roo.factory(this.view); 
50909         this.view.render  &&  this.view.render(false, '');  
50910     }
50911     
50912     
50913     this.fireEvent('render', this);
50914 };
50915
50916 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50917     tabTip:'',
50918     setRegion : function(region){
50919         this.region = region;
50920         if(region){
50921            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50922         }else{
50923            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50924         } 
50925     },
50926     
50927     /**
50928      * Returns the toolbar for this Panel if one was configured. 
50929      * @return {Roo.Toolbar} 
50930      */
50931     getToolbar : function(){
50932         return this.toolbar;
50933     },
50934     
50935     setActiveState : function(active){
50936         this.active = active;
50937         if(!active){
50938             this.fireEvent("deactivate", this);
50939         }else{
50940             this.fireEvent("activate", this);
50941         }
50942     },
50943     /**
50944      * Updates this panel's element
50945      * @param {String} content The new content
50946      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50947     */
50948     setContent : function(content, loadScripts){
50949         this.el.update(content, loadScripts);
50950     },
50951
50952     ignoreResize : function(w, h){
50953         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50954             return true;
50955         }else{
50956             this.lastSize = {width: w, height: h};
50957             return false;
50958         }
50959     },
50960     /**
50961      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50962      * @return {Roo.UpdateManager} The UpdateManager
50963      */
50964     getUpdateManager : function(){
50965         return this.el.getUpdateManager();
50966     },
50967      /**
50968      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50969      * @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:
50970 <pre><code>
50971 panel.load({
50972     url: "your-url.php",
50973     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50974     callback: yourFunction,
50975     scope: yourObject, //(optional scope)
50976     discardUrl: false,
50977     nocache: false,
50978     text: "Loading...",
50979     timeout: 30,
50980     scripts: false
50981 });
50982 </code></pre>
50983      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50984      * 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.
50985      * @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}
50986      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50987      * @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.
50988      * @return {Roo.ContentPanel} this
50989      */
50990     load : function(){
50991         var um = this.el.getUpdateManager();
50992         um.update.apply(um, arguments);
50993         return this;
50994     },
50995
50996
50997     /**
50998      * 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.
50999      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51000      * @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)
51001      * @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)
51002      * @return {Roo.UpdateManager} The UpdateManager
51003      */
51004     setUrl : function(url, params, loadOnce){
51005         if(this.refreshDelegate){
51006             this.removeListener("activate", this.refreshDelegate);
51007         }
51008         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51009         this.on("activate", this.refreshDelegate);
51010         return this.el.getUpdateManager();
51011     },
51012     
51013     _handleRefresh : function(url, params, loadOnce){
51014         if(!loadOnce || !this.loaded){
51015             var updater = this.el.getUpdateManager();
51016             updater.update(url, params, this._setLoaded.createDelegate(this));
51017         }
51018     },
51019     
51020     _setLoaded : function(){
51021         this.loaded = true;
51022     }, 
51023     
51024     /**
51025      * Returns this panel's id
51026      * @return {String} 
51027      */
51028     getId : function(){
51029         return this.el.id;
51030     },
51031     
51032     /** 
51033      * Returns this panel's element - used by regiosn to add.
51034      * @return {Roo.Element} 
51035      */
51036     getEl : function(){
51037         return this.wrapEl || this.el;
51038     },
51039     
51040     adjustForComponents : function(width, height)
51041     {
51042         //Roo.log('adjustForComponents ');
51043         if(this.resizeEl != this.el){
51044             width -= this.el.getFrameWidth('lr');
51045             height -= this.el.getFrameWidth('tb');
51046         }
51047         if(this.toolbar){
51048             var te = this.toolbar.getEl();
51049             height -= te.getHeight();
51050             te.setWidth(width);
51051         }
51052         if(this.footer){
51053             var te = this.footer.getEl();
51054             Roo.log("footer:" + te.getHeight());
51055             
51056             height -= te.getHeight();
51057             te.setWidth(width);
51058         }
51059         
51060         
51061         if(this.adjustments){
51062             width += this.adjustments[0];
51063             height += this.adjustments[1];
51064         }
51065         return {"width": width, "height": height};
51066     },
51067     
51068     setSize : function(width, height){
51069         if(this.fitToFrame && !this.ignoreResize(width, height)){
51070             if(this.fitContainer && this.resizeEl != this.el){
51071                 this.el.setSize(width, height);
51072             }
51073             var size = this.adjustForComponents(width, height);
51074             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51075             this.fireEvent('resize', this, size.width, size.height);
51076         }
51077     },
51078     
51079     /**
51080      * Returns this panel's title
51081      * @return {String} 
51082      */
51083     getTitle : function(){
51084         return this.title;
51085     },
51086     
51087     /**
51088      * Set this panel's title
51089      * @param {String} title
51090      */
51091     setTitle : function(title){
51092         this.title = title;
51093         if(this.region){
51094             this.region.updatePanelTitle(this, title);
51095         }
51096     },
51097     
51098     /**
51099      * Returns true is this panel was configured to be closable
51100      * @return {Boolean} 
51101      */
51102     isClosable : function(){
51103         return this.closable;
51104     },
51105     
51106     beforeSlide : function(){
51107         this.el.clip();
51108         this.resizeEl.clip();
51109     },
51110     
51111     afterSlide : function(){
51112         this.el.unclip();
51113         this.resizeEl.unclip();
51114     },
51115     
51116     /**
51117      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51118      *   Will fail silently if the {@link #setUrl} method has not been called.
51119      *   This does not activate the panel, just updates its content.
51120      */
51121     refresh : function(){
51122         if(this.refreshDelegate){
51123            this.loaded = false;
51124            this.refreshDelegate();
51125         }
51126     },
51127     
51128     /**
51129      * Destroys this panel
51130      */
51131     destroy : function(){
51132         this.el.removeAllListeners();
51133         var tempEl = document.createElement("span");
51134         tempEl.appendChild(this.el.dom);
51135         tempEl.innerHTML = "";
51136         this.el.remove();
51137         this.el = null;
51138     },
51139     
51140     /**
51141      * form - if the content panel contains a form - this is a reference to it.
51142      * @type {Roo.form.Form}
51143      */
51144     form : false,
51145     /**
51146      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51147      *    This contains a reference to it.
51148      * @type {Roo.View}
51149      */
51150     view : false,
51151     
51152       /**
51153      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51154      * <pre><code>
51155
51156 layout.addxtype({
51157        xtype : 'Form',
51158        items: [ .... ]
51159    }
51160 );
51161
51162 </code></pre>
51163      * @param {Object} cfg Xtype definition of item to add.
51164      */
51165     
51166     addxtype : function(cfg) {
51167         // add form..
51168         if (cfg.xtype.match(/^Form$/)) {
51169             
51170             var el;
51171             //if (this.footer) {
51172             //    el = this.footer.container.insertSibling(false, 'before');
51173             //} else {
51174                 el = this.el.createChild();
51175             //}
51176
51177             this.form = new  Roo.form.Form(cfg);
51178             
51179             
51180             if ( this.form.allItems.length) this.form.render(el.dom);
51181             return this.form;
51182         }
51183         // should only have one of theses..
51184         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51185             // views.. should not be just added - used named prop 'view''
51186             
51187             cfg.el = this.el.appendChild(document.createElement("div"));
51188             // factory?
51189             
51190             var ret = new Roo.factory(cfg);
51191              
51192              ret.render && ret.render(false, ''); // render blank..
51193             this.view = ret;
51194             return ret;
51195         }
51196         return false;
51197     }
51198 });
51199
51200 /**
51201  * @class Roo.GridPanel
51202  * @extends Roo.ContentPanel
51203  * @constructor
51204  * Create a new GridPanel.
51205  * @param {Roo.grid.Grid} grid The grid for this panel
51206  * @param {String/Object} config A string to set only the panel's title, or a config object
51207  */
51208 Roo.GridPanel = function(grid, config){
51209     
51210   
51211     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51212         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51213         
51214     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51215     
51216     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51217     
51218     if(this.toolbar){
51219         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51220     }
51221     // xtype created footer. - not sure if will work as we normally have to render first..
51222     if (this.footer && !this.footer.el && this.footer.xtype) {
51223         
51224         this.footer.container = this.grid.getView().getFooterPanel(true);
51225         this.footer.dataSource = this.grid.dataSource;
51226         this.footer = Roo.factory(this.footer, Roo);
51227         
51228     }
51229     
51230     grid.monitorWindowResize = false; // turn off autosizing
51231     grid.autoHeight = false;
51232     grid.autoWidth = false;
51233     this.grid = grid;
51234     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51235 };
51236
51237 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51238     getId : function(){
51239         return this.grid.id;
51240     },
51241     
51242     /**
51243      * Returns the grid for this panel
51244      * @return {Roo.grid.Grid} 
51245      */
51246     getGrid : function(){
51247         return this.grid;    
51248     },
51249     
51250     setSize : function(width, height){
51251         if(!this.ignoreResize(width, height)){
51252             var grid = this.grid;
51253             var size = this.adjustForComponents(width, height);
51254             grid.getGridEl().setSize(size.width, size.height);
51255             grid.autoSize();
51256         }
51257     },
51258     
51259     beforeSlide : function(){
51260         this.grid.getView().scroller.clip();
51261     },
51262     
51263     afterSlide : function(){
51264         this.grid.getView().scroller.unclip();
51265     },
51266     
51267     destroy : function(){
51268         this.grid.destroy();
51269         delete this.grid;
51270         Roo.GridPanel.superclass.destroy.call(this); 
51271     }
51272 });
51273
51274
51275 /**
51276  * @class Roo.NestedLayoutPanel
51277  * @extends Roo.ContentPanel
51278  * @constructor
51279  * Create a new NestedLayoutPanel.
51280  * 
51281  * 
51282  * @param {Roo.BorderLayout} layout The layout for this panel
51283  * @param {String/Object} config A string to set only the title or a config object
51284  */
51285 Roo.NestedLayoutPanel = function(layout, config)
51286 {
51287     // construct with only one argument..
51288     /* FIXME - implement nicer consturctors
51289     if (layout.layout) {
51290         config = layout;
51291         layout = config.layout;
51292         delete config.layout;
51293     }
51294     if (layout.xtype && !layout.getEl) {
51295         // then layout needs constructing..
51296         layout = Roo.factory(layout, Roo);
51297     }
51298     */
51299     
51300     
51301     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51302     
51303     layout.monitorWindowResize = false; // turn off autosizing
51304     this.layout = layout;
51305     this.layout.getEl().addClass("x-layout-nested-layout");
51306     
51307     
51308     
51309     
51310 };
51311
51312 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51313
51314     setSize : function(width, height){
51315         if(!this.ignoreResize(width, height)){
51316             var size = this.adjustForComponents(width, height);
51317             var el = this.layout.getEl();
51318             el.setSize(size.width, size.height);
51319             var touch = el.dom.offsetWidth;
51320             this.layout.layout();
51321             // ie requires a double layout on the first pass
51322             if(Roo.isIE && !this.initialized){
51323                 this.initialized = true;
51324                 this.layout.layout();
51325             }
51326         }
51327     },
51328     
51329     // activate all subpanels if not currently active..
51330     
51331     setActiveState : function(active){
51332         this.active = active;
51333         if(!active){
51334             this.fireEvent("deactivate", this);
51335             return;
51336         }
51337         
51338         this.fireEvent("activate", this);
51339         // not sure if this should happen before or after..
51340         if (!this.layout) {
51341             return; // should not happen..
51342         }
51343         var reg = false;
51344         for (var r in this.layout.regions) {
51345             reg = this.layout.getRegion(r);
51346             if (reg.getActivePanel()) {
51347                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51348                 reg.setActivePanel(reg.getActivePanel());
51349                 continue;
51350             }
51351             if (!reg.panels.length) {
51352                 continue;
51353             }
51354             reg.showPanel(reg.getPanel(0));
51355         }
51356         
51357         
51358         
51359         
51360     },
51361     
51362     /**
51363      * Returns the nested BorderLayout for this panel
51364      * @return {Roo.BorderLayout} 
51365      */
51366     getLayout : function(){
51367         return this.layout;
51368     },
51369     
51370      /**
51371      * Adds a xtype elements to the layout of the nested panel
51372      * <pre><code>
51373
51374 panel.addxtype({
51375        xtype : 'ContentPanel',
51376        region: 'west',
51377        items: [ .... ]
51378    }
51379 );
51380
51381 panel.addxtype({
51382         xtype : 'NestedLayoutPanel',
51383         region: 'west',
51384         layout: {
51385            center: { },
51386            west: { }   
51387         },
51388         items : [ ... list of content panels or nested layout panels.. ]
51389    }
51390 );
51391 </code></pre>
51392      * @param {Object} cfg Xtype definition of item to add.
51393      */
51394     addxtype : function(cfg) {
51395         return this.layout.addxtype(cfg);
51396     
51397     }
51398 });
51399
51400 Roo.ScrollPanel = function(el, config, content){
51401     config = config || {};
51402     config.fitToFrame = true;
51403     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51404     
51405     this.el.dom.style.overflow = "hidden";
51406     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51407     this.el.removeClass("x-layout-inactive-content");
51408     this.el.on("mousewheel", this.onWheel, this);
51409
51410     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51411     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51412     up.unselectable(); down.unselectable();
51413     up.on("click", this.scrollUp, this);
51414     down.on("click", this.scrollDown, this);
51415     up.addClassOnOver("x-scroller-btn-over");
51416     down.addClassOnOver("x-scroller-btn-over");
51417     up.addClassOnClick("x-scroller-btn-click");
51418     down.addClassOnClick("x-scroller-btn-click");
51419     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51420
51421     this.resizeEl = this.el;
51422     this.el = wrap; this.up = up; this.down = down;
51423 };
51424
51425 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51426     increment : 100,
51427     wheelIncrement : 5,
51428     scrollUp : function(){
51429         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51430     },
51431
51432     scrollDown : function(){
51433         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51434     },
51435
51436     afterScroll : function(){
51437         var el = this.resizeEl;
51438         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51439         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51440         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51441     },
51442
51443     setSize : function(){
51444         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51445         this.afterScroll();
51446     },
51447
51448     onWheel : function(e){
51449         var d = e.getWheelDelta();
51450         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51451         this.afterScroll();
51452         e.stopEvent();
51453     },
51454
51455     setContent : function(content, loadScripts){
51456         this.resizeEl.update(content, loadScripts);
51457     }
51458
51459 });
51460
51461
51462
51463
51464
51465
51466
51467
51468
51469 /**
51470  * @class Roo.TreePanel
51471  * @extends Roo.ContentPanel
51472  * @constructor
51473  * Create a new TreePanel. - defaults to fit/scoll contents.
51474  * @param {String/Object} config A string to set only the panel's title, or a config object
51475  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51476  */
51477 Roo.TreePanel = function(config){
51478     var el = config.el;
51479     var tree = config.tree;
51480     delete config.tree; 
51481     delete config.el; // hopefull!
51482     
51483     // wrapper for IE7 strict & safari scroll issue
51484     
51485     var treeEl = el.createChild();
51486     config.resizeEl = treeEl;
51487     
51488     
51489     
51490     Roo.TreePanel.superclass.constructor.call(this, el, config);
51491  
51492  
51493     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51494     //console.log(tree);
51495     this.on('activate', function()
51496     {
51497         if (this.tree.rendered) {
51498             return;
51499         }
51500         //console.log('render tree');
51501         this.tree.render();
51502     });
51503     // this should not be needed.. - it's actually the 'el' that resizes?
51504     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51505     
51506     //this.on('resize',  function (cp, w, h) {
51507     //        this.tree.innerCt.setWidth(w);
51508     //        this.tree.innerCt.setHeight(h);
51509     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51510     //});
51511
51512         
51513     
51514 };
51515
51516 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51517     fitToFrame : true,
51518     autoScroll : true
51519 });
51520
51521
51522
51523
51524
51525
51526
51527
51528
51529
51530
51531 /*
51532  * Based on:
51533  * Ext JS Library 1.1.1
51534  * Copyright(c) 2006-2007, Ext JS, LLC.
51535  *
51536  * Originally Released Under LGPL - original licence link has changed is not relivant.
51537  *
51538  * Fork - LGPL
51539  * <script type="text/javascript">
51540  */
51541  
51542
51543 /**
51544  * @class Roo.ReaderLayout
51545  * @extends Roo.BorderLayout
51546  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51547  * center region containing two nested regions (a top one for a list view and one for item preview below),
51548  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51549  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51550  * expedites the setup of the overall layout and regions for this common application style.
51551  * Example:
51552  <pre><code>
51553 var reader = new Roo.ReaderLayout();
51554 var CP = Roo.ContentPanel;  // shortcut for adding
51555
51556 reader.beginUpdate();
51557 reader.add("north", new CP("north", "North"));
51558 reader.add("west", new CP("west", {title: "West"}));
51559 reader.add("east", new CP("east", {title: "East"}));
51560
51561 reader.regions.listView.add(new CP("listView", "List"));
51562 reader.regions.preview.add(new CP("preview", "Preview"));
51563 reader.endUpdate();
51564 </code></pre>
51565 * @constructor
51566 * Create a new ReaderLayout
51567 * @param {Object} config Configuration options
51568 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51569 * document.body if omitted)
51570 */
51571 Roo.ReaderLayout = function(config, renderTo){
51572     var c = config || {size:{}};
51573     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51574         north: c.north !== false ? Roo.apply({
51575             split:false,
51576             initialSize: 32,
51577             titlebar: false
51578         }, c.north) : false,
51579         west: c.west !== false ? Roo.apply({
51580             split:true,
51581             initialSize: 200,
51582             minSize: 175,
51583             maxSize: 400,
51584             titlebar: true,
51585             collapsible: true,
51586             animate: true,
51587             margins:{left:5,right:0,bottom:5,top:5},
51588             cmargins:{left:5,right:5,bottom:5,top:5}
51589         }, c.west) : false,
51590         east: c.east !== false ? Roo.apply({
51591             split:true,
51592             initialSize: 200,
51593             minSize: 175,
51594             maxSize: 400,
51595             titlebar: true,
51596             collapsible: true,
51597             animate: true,
51598             margins:{left:0,right:5,bottom:5,top:5},
51599             cmargins:{left:5,right:5,bottom:5,top:5}
51600         }, c.east) : false,
51601         center: Roo.apply({
51602             tabPosition: 'top',
51603             autoScroll:false,
51604             closeOnTab: true,
51605             titlebar:false,
51606             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51607         }, c.center)
51608     });
51609
51610     this.el.addClass('x-reader');
51611
51612     this.beginUpdate();
51613
51614     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51615         south: c.preview !== false ? Roo.apply({
51616             split:true,
51617             initialSize: 200,
51618             minSize: 100,
51619             autoScroll:true,
51620             collapsible:true,
51621             titlebar: true,
51622             cmargins:{top:5,left:0, right:0, bottom:0}
51623         }, c.preview) : false,
51624         center: Roo.apply({
51625             autoScroll:false,
51626             titlebar:false,
51627             minHeight:200
51628         }, c.listView)
51629     });
51630     this.add('center', new Roo.NestedLayoutPanel(inner,
51631             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51632
51633     this.endUpdate();
51634
51635     this.regions.preview = inner.getRegion('south');
51636     this.regions.listView = inner.getRegion('center');
51637 };
51638
51639 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51640  * Based on:
51641  * Ext JS Library 1.1.1
51642  * Copyright(c) 2006-2007, Ext JS, LLC.
51643  *
51644  * Originally Released Under LGPL - original licence link has changed is not relivant.
51645  *
51646  * Fork - LGPL
51647  * <script type="text/javascript">
51648  */
51649  
51650 /**
51651  * @class Roo.grid.Grid
51652  * @extends Roo.util.Observable
51653  * This class represents the primary interface of a component based grid control.
51654  * <br><br>Usage:<pre><code>
51655  var grid = new Roo.grid.Grid("my-container-id", {
51656      ds: myDataStore,
51657      cm: myColModel,
51658      selModel: mySelectionModel,
51659      autoSizeColumns: true,
51660      monitorWindowResize: false,
51661      trackMouseOver: true
51662  });
51663  // set any options
51664  grid.render();
51665  * </code></pre>
51666  * <b>Common Problems:</b><br/>
51667  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51668  * element will correct this<br/>
51669  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51670  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51671  * are unpredictable.<br/>
51672  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51673  * grid to calculate dimensions/offsets.<br/>
51674   * @constructor
51675  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51676  * The container MUST have some type of size defined for the grid to fill. The container will be
51677  * automatically set to position relative if it isn't already.
51678  * @param {Object} config A config object that sets properties on this grid.
51679  */
51680 Roo.grid.Grid = function(container, config){
51681         // initialize the container
51682         this.container = Roo.get(container);
51683         this.container.update("");
51684         this.container.setStyle("overflow", "hidden");
51685     this.container.addClass('x-grid-container');
51686
51687     this.id = this.container.id;
51688
51689     Roo.apply(this, config);
51690     // check and correct shorthanded configs
51691     if(this.ds){
51692         this.dataSource = this.ds;
51693         delete this.ds;
51694     }
51695     if(this.cm){
51696         this.colModel = this.cm;
51697         delete this.cm;
51698     }
51699     if(this.sm){
51700         this.selModel = this.sm;
51701         delete this.sm;
51702     }
51703
51704     if (this.selModel) {
51705         this.selModel = Roo.factory(this.selModel, Roo.grid);
51706         this.sm = this.selModel;
51707         this.sm.xmodule = this.xmodule || false;
51708     }
51709     if (typeof(this.colModel.config) == 'undefined') {
51710         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51711         this.cm = this.colModel;
51712         this.cm.xmodule = this.xmodule || false;
51713     }
51714     if (this.dataSource) {
51715         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51716         this.ds = this.dataSource;
51717         this.ds.xmodule = this.xmodule || false;
51718          
51719     }
51720     
51721     
51722     
51723     if(this.width){
51724         this.container.setWidth(this.width);
51725     }
51726
51727     if(this.height){
51728         this.container.setHeight(this.height);
51729     }
51730     /** @private */
51731         this.addEvents({
51732         // raw events
51733         /**
51734          * @event click
51735          * The raw click event for the entire grid.
51736          * @param {Roo.EventObject} e
51737          */
51738         "click" : true,
51739         /**
51740          * @event dblclick
51741          * The raw dblclick event for the entire grid.
51742          * @param {Roo.EventObject} e
51743          */
51744         "dblclick" : true,
51745         /**
51746          * @event contextmenu
51747          * The raw contextmenu event for the entire grid.
51748          * @param {Roo.EventObject} e
51749          */
51750         "contextmenu" : true,
51751         /**
51752          * @event mousedown
51753          * The raw mousedown event for the entire grid.
51754          * @param {Roo.EventObject} e
51755          */
51756         "mousedown" : true,
51757         /**
51758          * @event mouseup
51759          * The raw mouseup event for the entire grid.
51760          * @param {Roo.EventObject} e
51761          */
51762         "mouseup" : true,
51763         /**
51764          * @event mouseover
51765          * The raw mouseover event for the entire grid.
51766          * @param {Roo.EventObject} e
51767          */
51768         "mouseover" : true,
51769         /**
51770          * @event mouseout
51771          * The raw mouseout event for the entire grid.
51772          * @param {Roo.EventObject} e
51773          */
51774         "mouseout" : true,
51775         /**
51776          * @event keypress
51777          * The raw keypress event for the entire grid.
51778          * @param {Roo.EventObject} e
51779          */
51780         "keypress" : true,
51781         /**
51782          * @event keydown
51783          * The raw keydown event for the entire grid.
51784          * @param {Roo.EventObject} e
51785          */
51786         "keydown" : true,
51787
51788         // custom events
51789
51790         /**
51791          * @event cellclick
51792          * Fires when a cell is clicked
51793          * @param {Grid} this
51794          * @param {Number} rowIndex
51795          * @param {Number} columnIndex
51796          * @param {Roo.EventObject} e
51797          */
51798         "cellclick" : true,
51799         /**
51800          * @event celldblclick
51801          * Fires when a cell is double clicked
51802          * @param {Grid} this
51803          * @param {Number} rowIndex
51804          * @param {Number} columnIndex
51805          * @param {Roo.EventObject} e
51806          */
51807         "celldblclick" : true,
51808         /**
51809          * @event rowclick
51810          * Fires when a row is clicked
51811          * @param {Grid} this
51812          * @param {Number} rowIndex
51813          * @param {Roo.EventObject} e
51814          */
51815         "rowclick" : true,
51816         /**
51817          * @event rowdblclick
51818          * Fires when a row is double clicked
51819          * @param {Grid} this
51820          * @param {Number} rowIndex
51821          * @param {Roo.EventObject} e
51822          */
51823         "rowdblclick" : true,
51824         /**
51825          * @event headerclick
51826          * Fires when a header is clicked
51827          * @param {Grid} this
51828          * @param {Number} columnIndex
51829          * @param {Roo.EventObject} e
51830          */
51831         "headerclick" : true,
51832         /**
51833          * @event headerdblclick
51834          * Fires when a header cell is double clicked
51835          * @param {Grid} this
51836          * @param {Number} columnIndex
51837          * @param {Roo.EventObject} e
51838          */
51839         "headerdblclick" : true,
51840         /**
51841          * @event rowcontextmenu
51842          * Fires when a row is right clicked
51843          * @param {Grid} this
51844          * @param {Number} rowIndex
51845          * @param {Roo.EventObject} e
51846          */
51847         "rowcontextmenu" : true,
51848         /**
51849          * @event cellcontextmenu
51850          * Fires when a cell is right clicked
51851          * @param {Grid} this
51852          * @param {Number} rowIndex
51853          * @param {Number} cellIndex
51854          * @param {Roo.EventObject} e
51855          */
51856          "cellcontextmenu" : true,
51857         /**
51858          * @event headercontextmenu
51859          * Fires when a header is right clicked
51860          * @param {Grid} this
51861          * @param {Number} columnIndex
51862          * @param {Roo.EventObject} e
51863          */
51864         "headercontextmenu" : true,
51865         /**
51866          * @event bodyscroll
51867          * Fires when the body element is scrolled
51868          * @param {Number} scrollLeft
51869          * @param {Number} scrollTop
51870          */
51871         "bodyscroll" : true,
51872         /**
51873          * @event columnresize
51874          * Fires when the user resizes a column
51875          * @param {Number} columnIndex
51876          * @param {Number} newSize
51877          */
51878         "columnresize" : true,
51879         /**
51880          * @event columnmove
51881          * Fires when the user moves a column
51882          * @param {Number} oldIndex
51883          * @param {Number} newIndex
51884          */
51885         "columnmove" : true,
51886         /**
51887          * @event startdrag
51888          * Fires when row(s) start being dragged
51889          * @param {Grid} this
51890          * @param {Roo.GridDD} dd The drag drop object
51891          * @param {event} e The raw browser event
51892          */
51893         "startdrag" : true,
51894         /**
51895          * @event enddrag
51896          * Fires when a drag operation is complete
51897          * @param {Grid} this
51898          * @param {Roo.GridDD} dd The drag drop object
51899          * @param {event} e The raw browser event
51900          */
51901         "enddrag" : true,
51902         /**
51903          * @event dragdrop
51904          * Fires when dragged row(s) are dropped on a valid DD target
51905          * @param {Grid} this
51906          * @param {Roo.GridDD} dd The drag drop object
51907          * @param {String} targetId The target drag drop object
51908          * @param {event} e The raw browser event
51909          */
51910         "dragdrop" : true,
51911         /**
51912          * @event dragover
51913          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51914          * @param {Grid} this
51915          * @param {Roo.GridDD} dd The drag drop object
51916          * @param {String} targetId The target drag drop object
51917          * @param {event} e The raw browser event
51918          */
51919         "dragover" : true,
51920         /**
51921          * @event dragenter
51922          *  Fires when the dragged row(s) first cross another DD target while being dragged
51923          * @param {Grid} this
51924          * @param {Roo.GridDD} dd The drag drop object
51925          * @param {String} targetId The target drag drop object
51926          * @param {event} e The raw browser event
51927          */
51928         "dragenter" : true,
51929         /**
51930          * @event dragout
51931          * Fires when the dragged row(s) leave another DD target while being dragged
51932          * @param {Grid} this
51933          * @param {Roo.GridDD} dd The drag drop object
51934          * @param {String} targetId The target drag drop object
51935          * @param {event} e The raw browser event
51936          */
51937         "dragout" : true,
51938         /**
51939          * @event rowclass
51940          * Fires when a row is rendered, so you can change add a style to it.
51941          * @param {GridView} gridview   The grid view
51942          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51943          */
51944         'rowclass' : true,
51945
51946         /**
51947          * @event render
51948          * Fires when the grid is rendered
51949          * @param {Grid} grid
51950          */
51951         'render' : true
51952     });
51953
51954     Roo.grid.Grid.superclass.constructor.call(this);
51955 };
51956 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51957     
51958     /**
51959      * @cfg {String} ddGroup - drag drop group.
51960      */
51961
51962     /**
51963      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51964      */
51965     minColumnWidth : 25,
51966
51967     /**
51968      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51969      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51970      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51971      */
51972     autoSizeColumns : false,
51973
51974     /**
51975      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51976      */
51977     autoSizeHeaders : true,
51978
51979     /**
51980      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51981      */
51982     monitorWindowResize : true,
51983
51984     /**
51985      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51986      * rows measured to get a columns size. Default is 0 (all rows).
51987      */
51988     maxRowsToMeasure : 0,
51989
51990     /**
51991      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51992      */
51993     trackMouseOver : true,
51994
51995     /**
51996     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51997     */
51998     
51999     /**
52000     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52001     */
52002     enableDragDrop : false,
52003     
52004     /**
52005     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52006     */
52007     enableColumnMove : true,
52008     
52009     /**
52010     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52011     */
52012     enableColumnHide : true,
52013     
52014     /**
52015     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52016     */
52017     enableRowHeightSync : false,
52018     
52019     /**
52020     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52021     */
52022     stripeRows : true,
52023     
52024     /**
52025     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52026     */
52027     autoHeight : false,
52028
52029     /**
52030      * @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.
52031      */
52032     autoExpandColumn : false,
52033
52034     /**
52035     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52036     * Default is 50.
52037     */
52038     autoExpandMin : 50,
52039
52040     /**
52041     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52042     */
52043     autoExpandMax : 1000,
52044
52045     /**
52046     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52047     */
52048     view : null,
52049
52050     /**
52051     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52052     */
52053     loadMask : false,
52054     /**
52055     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52056     */
52057     dropTarget: false,
52058     
52059    
52060     
52061     // private
52062     rendered : false,
52063
52064     /**
52065     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52066     * of a fixed width. Default is false.
52067     */
52068     /**
52069     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52070     */
52071     /**
52072      * Called once after all setup has been completed and the grid is ready to be rendered.
52073      * @return {Roo.grid.Grid} this
52074      */
52075     render : function()
52076     {
52077         var c = this.container;
52078         // try to detect autoHeight/width mode
52079         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52080             this.autoHeight = true;
52081         }
52082         var view = this.getView();
52083         view.init(this);
52084
52085         c.on("click", this.onClick, this);
52086         c.on("dblclick", this.onDblClick, this);
52087         c.on("contextmenu", this.onContextMenu, this);
52088         c.on("keydown", this.onKeyDown, this);
52089         if (Roo.isTouch) {
52090             c.on("touchstart", this.onTouchStart, this);
52091         }
52092
52093         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52094
52095         this.getSelectionModel().init(this);
52096
52097         view.render();
52098
52099         if(this.loadMask){
52100             this.loadMask = new Roo.LoadMask(this.container,
52101                     Roo.apply({store:this.dataSource}, this.loadMask));
52102         }
52103         
52104         
52105         if (this.toolbar && this.toolbar.xtype) {
52106             this.toolbar.container = this.getView().getHeaderPanel(true);
52107             this.toolbar = new Roo.Toolbar(this.toolbar);
52108         }
52109         if (this.footer && this.footer.xtype) {
52110             this.footer.dataSource = this.getDataSource();
52111             this.footer.container = this.getView().getFooterPanel(true);
52112             this.footer = Roo.factory(this.footer, Roo);
52113         }
52114         if (this.dropTarget && this.dropTarget.xtype) {
52115             delete this.dropTarget.xtype;
52116             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52117         }
52118         
52119         
52120         this.rendered = true;
52121         this.fireEvent('render', this);
52122         return this;
52123     },
52124
52125         /**
52126          * Reconfigures the grid to use a different Store and Column Model.
52127          * The View will be bound to the new objects and refreshed.
52128          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52129          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52130          */
52131     reconfigure : function(dataSource, colModel){
52132         if(this.loadMask){
52133             this.loadMask.destroy();
52134             this.loadMask = new Roo.LoadMask(this.container,
52135                     Roo.apply({store:dataSource}, this.loadMask));
52136         }
52137         this.view.bind(dataSource, colModel);
52138         this.dataSource = dataSource;
52139         this.colModel = colModel;
52140         this.view.refresh(true);
52141     },
52142
52143     // private
52144     onKeyDown : function(e){
52145         this.fireEvent("keydown", e);
52146     },
52147
52148     /**
52149      * Destroy this grid.
52150      * @param {Boolean} removeEl True to remove the element
52151      */
52152     destroy : function(removeEl, keepListeners){
52153         if(this.loadMask){
52154             this.loadMask.destroy();
52155         }
52156         var c = this.container;
52157         c.removeAllListeners();
52158         this.view.destroy();
52159         this.colModel.purgeListeners();
52160         if(!keepListeners){
52161             this.purgeListeners();
52162         }
52163         c.update("");
52164         if(removeEl === true){
52165             c.remove();
52166         }
52167     },
52168
52169     // private
52170     processEvent : function(name, e){
52171         // does this fire select???
52172         Roo.log('grid:processEvent '  + name);
52173         
52174         if (name != 'touchstart' ) {
52175             this.fireEvent(name, e);    
52176         }
52177         
52178         var t = e.getTarget();
52179         var v = this.view;
52180         var header = v.findHeaderIndex(t);
52181         if(header !== false){
52182             var ename = name == 'touchstart' ? 'click' : name;
52183              
52184             this.fireEvent("header" + ename, this, header, e);
52185         }else{
52186             var row = v.findRowIndex(t);
52187             var cell = v.findCellIndex(t);
52188             if (name == 'touchstart') {
52189                 // first touch is always a click.
52190                 // hopefull this happens after selection is updated.?
52191                 name = false;
52192                 
52193                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52194                     var cs = this.selModel.getSelectedCell();
52195                     if (row == cs[0] && cell == cs[1]){
52196                         name = 'dblclick';
52197                     }
52198                 }
52199                 if (typeof(this.selModel.getSelections) != 'undefined') {
52200                     var cs = this.selModel.getSelections();
52201                     var ds = this.dataSource;
52202                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52203                         name = 'dblclick';
52204                     }
52205                 }
52206                 if (!name) {
52207                     return;
52208                 }
52209             }
52210             
52211             
52212             if(row !== false){
52213                 this.fireEvent("row" + name, this, row, e);
52214                 if(cell !== false){
52215                     this.fireEvent("cell" + name, this, row, cell, e);
52216                 }
52217             }
52218         }
52219     },
52220
52221     // private
52222     onClick : function(e){
52223         this.processEvent("click", e);
52224     },
52225    // private
52226     onTouchStart : function(e){
52227         this.processEvent("touchstart", e);
52228     },
52229
52230     // private
52231     onContextMenu : function(e, t){
52232         this.processEvent("contextmenu", e);
52233     },
52234
52235     // private
52236     onDblClick : function(e){
52237         this.processEvent("dblclick", e);
52238     },
52239
52240     // private
52241     walkCells : function(row, col, step, fn, scope){
52242         var cm = this.colModel, clen = cm.getColumnCount();
52243         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52244         if(step < 0){
52245             if(col < 0){
52246                 row--;
52247                 first = false;
52248             }
52249             while(row >= 0){
52250                 if(!first){
52251                     col = clen-1;
52252                 }
52253                 first = false;
52254                 while(col >= 0){
52255                     if(fn.call(scope || this, row, col, cm) === true){
52256                         return [row, col];
52257                     }
52258                     col--;
52259                 }
52260                 row--;
52261             }
52262         } else {
52263             if(col >= clen){
52264                 row++;
52265                 first = false;
52266             }
52267             while(row < rlen){
52268                 if(!first){
52269                     col = 0;
52270                 }
52271                 first = false;
52272                 while(col < clen){
52273                     if(fn.call(scope || this, row, col, cm) === true){
52274                         return [row, col];
52275                     }
52276                     col++;
52277                 }
52278                 row++;
52279             }
52280         }
52281         return null;
52282     },
52283
52284     // private
52285     getSelections : function(){
52286         return this.selModel.getSelections();
52287     },
52288
52289     /**
52290      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52291      * but if manual update is required this method will initiate it.
52292      */
52293     autoSize : function(){
52294         if(this.rendered){
52295             this.view.layout();
52296             if(this.view.adjustForScroll){
52297                 this.view.adjustForScroll();
52298             }
52299         }
52300     },
52301
52302     /**
52303      * Returns the grid's underlying element.
52304      * @return {Element} The element
52305      */
52306     getGridEl : function(){
52307         return this.container;
52308     },
52309
52310     // private for compatibility, overridden by editor grid
52311     stopEditing : function(){},
52312
52313     /**
52314      * Returns the grid's SelectionModel.
52315      * @return {SelectionModel}
52316      */
52317     getSelectionModel : function(){
52318         if(!this.selModel){
52319             this.selModel = new Roo.grid.RowSelectionModel();
52320         }
52321         return this.selModel;
52322     },
52323
52324     /**
52325      * Returns the grid's DataSource.
52326      * @return {DataSource}
52327      */
52328     getDataSource : function(){
52329         return this.dataSource;
52330     },
52331
52332     /**
52333      * Returns the grid's ColumnModel.
52334      * @return {ColumnModel}
52335      */
52336     getColumnModel : function(){
52337         return this.colModel;
52338     },
52339
52340     /**
52341      * Returns the grid's GridView object.
52342      * @return {GridView}
52343      */
52344     getView : function(){
52345         if(!this.view){
52346             this.view = new Roo.grid.GridView(this.viewConfig);
52347         }
52348         return this.view;
52349     },
52350     /**
52351      * Called to get grid's drag proxy text, by default returns this.ddText.
52352      * @return {String}
52353      */
52354     getDragDropText : function(){
52355         var count = this.selModel.getCount();
52356         return String.format(this.ddText, count, count == 1 ? '' : 's');
52357     }
52358 });
52359 /**
52360  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52361  * %0 is replaced with the number of selected rows.
52362  * @type String
52363  */
52364 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52365  * Based on:
52366  * Ext JS Library 1.1.1
52367  * Copyright(c) 2006-2007, Ext JS, LLC.
52368  *
52369  * Originally Released Under LGPL - original licence link has changed is not relivant.
52370  *
52371  * Fork - LGPL
52372  * <script type="text/javascript">
52373  */
52374  
52375 Roo.grid.AbstractGridView = function(){
52376         this.grid = null;
52377         
52378         this.events = {
52379             "beforerowremoved" : true,
52380             "beforerowsinserted" : true,
52381             "beforerefresh" : true,
52382             "rowremoved" : true,
52383             "rowsinserted" : true,
52384             "rowupdated" : true,
52385             "refresh" : true
52386         };
52387     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52388 };
52389
52390 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52391     rowClass : "x-grid-row",
52392     cellClass : "x-grid-cell",
52393     tdClass : "x-grid-td",
52394     hdClass : "x-grid-hd",
52395     splitClass : "x-grid-hd-split",
52396     
52397     init: function(grid){
52398         this.grid = grid;
52399                 var cid = this.grid.getGridEl().id;
52400         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52401         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52402         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52403         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52404         },
52405         
52406     getColumnRenderers : function(){
52407         var renderers = [];
52408         var cm = this.grid.colModel;
52409         var colCount = cm.getColumnCount();
52410         for(var i = 0; i < colCount; i++){
52411             renderers[i] = cm.getRenderer(i);
52412         }
52413         return renderers;
52414     },
52415     
52416     getColumnIds : function(){
52417         var ids = [];
52418         var cm = this.grid.colModel;
52419         var colCount = cm.getColumnCount();
52420         for(var i = 0; i < colCount; i++){
52421             ids[i] = cm.getColumnId(i);
52422         }
52423         return ids;
52424     },
52425     
52426     getDataIndexes : function(){
52427         if(!this.indexMap){
52428             this.indexMap = this.buildIndexMap();
52429         }
52430         return this.indexMap.colToData;
52431     },
52432     
52433     getColumnIndexByDataIndex : function(dataIndex){
52434         if(!this.indexMap){
52435             this.indexMap = this.buildIndexMap();
52436         }
52437         return this.indexMap.dataToCol[dataIndex];
52438     },
52439     
52440     /**
52441      * Set a css style for a column dynamically. 
52442      * @param {Number} colIndex The index of the column
52443      * @param {String} name The css property name
52444      * @param {String} value The css value
52445      */
52446     setCSSStyle : function(colIndex, name, value){
52447         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52448         Roo.util.CSS.updateRule(selector, name, value);
52449     },
52450     
52451     generateRules : function(cm){
52452         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52453         Roo.util.CSS.removeStyleSheet(rulesId);
52454         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52455             var cid = cm.getColumnId(i);
52456             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52457                          this.tdSelector, cid, " {\n}\n",
52458                          this.hdSelector, cid, " {\n}\n",
52459                          this.splitSelector, cid, " {\n}\n");
52460         }
52461         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52462     }
52463 });/*
52464  * Based on:
52465  * Ext JS Library 1.1.1
52466  * Copyright(c) 2006-2007, Ext JS, LLC.
52467  *
52468  * Originally Released Under LGPL - original licence link has changed is not relivant.
52469  *
52470  * Fork - LGPL
52471  * <script type="text/javascript">
52472  */
52473
52474 // private
52475 // This is a support class used internally by the Grid components
52476 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52477     this.grid = grid;
52478     this.view = grid.getView();
52479     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52480     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52481     if(hd2){
52482         this.setHandleElId(Roo.id(hd));
52483         this.setOuterHandleElId(Roo.id(hd2));
52484     }
52485     this.scroll = false;
52486 };
52487 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52488     maxDragWidth: 120,
52489     getDragData : function(e){
52490         var t = Roo.lib.Event.getTarget(e);
52491         var h = this.view.findHeaderCell(t);
52492         if(h){
52493             return {ddel: h.firstChild, header:h};
52494         }
52495         return false;
52496     },
52497
52498     onInitDrag : function(e){
52499         this.view.headersDisabled = true;
52500         var clone = this.dragData.ddel.cloneNode(true);
52501         clone.id = Roo.id();
52502         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52503         this.proxy.update(clone);
52504         return true;
52505     },
52506
52507     afterValidDrop : function(){
52508         var v = this.view;
52509         setTimeout(function(){
52510             v.headersDisabled = false;
52511         }, 50);
52512     },
52513
52514     afterInvalidDrop : function(){
52515         var v = this.view;
52516         setTimeout(function(){
52517             v.headersDisabled = false;
52518         }, 50);
52519     }
52520 });
52521 /*
52522  * Based on:
52523  * Ext JS Library 1.1.1
52524  * Copyright(c) 2006-2007, Ext JS, LLC.
52525  *
52526  * Originally Released Under LGPL - original licence link has changed is not relivant.
52527  *
52528  * Fork - LGPL
52529  * <script type="text/javascript">
52530  */
52531 // private
52532 // This is a support class used internally by the Grid components
52533 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52534     this.grid = grid;
52535     this.view = grid.getView();
52536     // split the proxies so they don't interfere with mouse events
52537     this.proxyTop = Roo.DomHelper.append(document.body, {
52538         cls:"col-move-top", html:"&#160;"
52539     }, true);
52540     this.proxyBottom = Roo.DomHelper.append(document.body, {
52541         cls:"col-move-bottom", html:"&#160;"
52542     }, true);
52543     this.proxyTop.hide = this.proxyBottom.hide = function(){
52544         this.setLeftTop(-100,-100);
52545         this.setStyle("visibility", "hidden");
52546     };
52547     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52548     // temporarily disabled
52549     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52550     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52551 };
52552 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52553     proxyOffsets : [-4, -9],
52554     fly: Roo.Element.fly,
52555
52556     getTargetFromEvent : function(e){
52557         var t = Roo.lib.Event.getTarget(e);
52558         var cindex = this.view.findCellIndex(t);
52559         if(cindex !== false){
52560             return this.view.getHeaderCell(cindex);
52561         }
52562         return null;
52563     },
52564
52565     nextVisible : function(h){
52566         var v = this.view, cm = this.grid.colModel;
52567         h = h.nextSibling;
52568         while(h){
52569             if(!cm.isHidden(v.getCellIndex(h))){
52570                 return h;
52571             }
52572             h = h.nextSibling;
52573         }
52574         return null;
52575     },
52576
52577     prevVisible : function(h){
52578         var v = this.view, cm = this.grid.colModel;
52579         h = h.prevSibling;
52580         while(h){
52581             if(!cm.isHidden(v.getCellIndex(h))){
52582                 return h;
52583             }
52584             h = h.prevSibling;
52585         }
52586         return null;
52587     },
52588
52589     positionIndicator : function(h, n, e){
52590         var x = Roo.lib.Event.getPageX(e);
52591         var r = Roo.lib.Dom.getRegion(n.firstChild);
52592         var px, pt, py = r.top + this.proxyOffsets[1];
52593         if((r.right - x) <= (r.right-r.left)/2){
52594             px = r.right+this.view.borderWidth;
52595             pt = "after";
52596         }else{
52597             px = r.left;
52598             pt = "before";
52599         }
52600         var oldIndex = this.view.getCellIndex(h);
52601         var newIndex = this.view.getCellIndex(n);
52602
52603         if(this.grid.colModel.isFixed(newIndex)){
52604             return false;
52605         }
52606
52607         var locked = this.grid.colModel.isLocked(newIndex);
52608
52609         if(pt == "after"){
52610             newIndex++;
52611         }
52612         if(oldIndex < newIndex){
52613             newIndex--;
52614         }
52615         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52616             return false;
52617         }
52618         px +=  this.proxyOffsets[0];
52619         this.proxyTop.setLeftTop(px, py);
52620         this.proxyTop.show();
52621         if(!this.bottomOffset){
52622             this.bottomOffset = this.view.mainHd.getHeight();
52623         }
52624         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52625         this.proxyBottom.show();
52626         return pt;
52627     },
52628
52629     onNodeEnter : function(n, dd, e, data){
52630         if(data.header != n){
52631             this.positionIndicator(data.header, n, e);
52632         }
52633     },
52634
52635     onNodeOver : function(n, dd, e, data){
52636         var result = false;
52637         if(data.header != n){
52638             result = this.positionIndicator(data.header, n, e);
52639         }
52640         if(!result){
52641             this.proxyTop.hide();
52642             this.proxyBottom.hide();
52643         }
52644         return result ? this.dropAllowed : this.dropNotAllowed;
52645     },
52646
52647     onNodeOut : function(n, dd, e, data){
52648         this.proxyTop.hide();
52649         this.proxyBottom.hide();
52650     },
52651
52652     onNodeDrop : function(n, dd, e, data){
52653         var h = data.header;
52654         if(h != n){
52655             var cm = this.grid.colModel;
52656             var x = Roo.lib.Event.getPageX(e);
52657             var r = Roo.lib.Dom.getRegion(n.firstChild);
52658             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52659             var oldIndex = this.view.getCellIndex(h);
52660             var newIndex = this.view.getCellIndex(n);
52661             var locked = cm.isLocked(newIndex);
52662             if(pt == "after"){
52663                 newIndex++;
52664             }
52665             if(oldIndex < newIndex){
52666                 newIndex--;
52667             }
52668             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52669                 return false;
52670             }
52671             cm.setLocked(oldIndex, locked, true);
52672             cm.moveColumn(oldIndex, newIndex);
52673             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52674             return true;
52675         }
52676         return false;
52677     }
52678 });
52679 /*
52680  * Based on:
52681  * Ext JS Library 1.1.1
52682  * Copyright(c) 2006-2007, Ext JS, LLC.
52683  *
52684  * Originally Released Under LGPL - original licence link has changed is not relivant.
52685  *
52686  * Fork - LGPL
52687  * <script type="text/javascript">
52688  */
52689   
52690 /**
52691  * @class Roo.grid.GridView
52692  * @extends Roo.util.Observable
52693  *
52694  * @constructor
52695  * @param {Object} config
52696  */
52697 Roo.grid.GridView = function(config){
52698     Roo.grid.GridView.superclass.constructor.call(this);
52699     this.el = null;
52700
52701     Roo.apply(this, config);
52702 };
52703
52704 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52705
52706     unselectable :  'unselectable="on"',
52707     unselectableCls :  'x-unselectable',
52708     
52709     
52710     rowClass : "x-grid-row",
52711
52712     cellClass : "x-grid-col",
52713
52714     tdClass : "x-grid-td",
52715
52716     hdClass : "x-grid-hd",
52717
52718     splitClass : "x-grid-split",
52719
52720     sortClasses : ["sort-asc", "sort-desc"],
52721
52722     enableMoveAnim : false,
52723
52724     hlColor: "C3DAF9",
52725
52726     dh : Roo.DomHelper,
52727
52728     fly : Roo.Element.fly,
52729
52730     css : Roo.util.CSS,
52731
52732     borderWidth: 1,
52733
52734     splitOffset: 3,
52735
52736     scrollIncrement : 22,
52737
52738     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52739
52740     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52741
52742     bind : function(ds, cm){
52743         if(this.ds){
52744             this.ds.un("load", this.onLoad, this);
52745             this.ds.un("datachanged", this.onDataChange, this);
52746             this.ds.un("add", this.onAdd, this);
52747             this.ds.un("remove", this.onRemove, this);
52748             this.ds.un("update", this.onUpdate, this);
52749             this.ds.un("clear", this.onClear, this);
52750         }
52751         if(ds){
52752             ds.on("load", this.onLoad, this);
52753             ds.on("datachanged", this.onDataChange, this);
52754             ds.on("add", this.onAdd, this);
52755             ds.on("remove", this.onRemove, this);
52756             ds.on("update", this.onUpdate, this);
52757             ds.on("clear", this.onClear, this);
52758         }
52759         this.ds = ds;
52760
52761         if(this.cm){
52762             this.cm.un("widthchange", this.onColWidthChange, this);
52763             this.cm.un("headerchange", this.onHeaderChange, this);
52764             this.cm.un("hiddenchange", this.onHiddenChange, this);
52765             this.cm.un("columnmoved", this.onColumnMove, this);
52766             this.cm.un("columnlockchange", this.onColumnLock, this);
52767         }
52768         if(cm){
52769             this.generateRules(cm);
52770             cm.on("widthchange", this.onColWidthChange, this);
52771             cm.on("headerchange", this.onHeaderChange, this);
52772             cm.on("hiddenchange", this.onHiddenChange, this);
52773             cm.on("columnmoved", this.onColumnMove, this);
52774             cm.on("columnlockchange", this.onColumnLock, this);
52775         }
52776         this.cm = cm;
52777     },
52778
52779     init: function(grid){
52780         Roo.grid.GridView.superclass.init.call(this, grid);
52781
52782         this.bind(grid.dataSource, grid.colModel);
52783
52784         grid.on("headerclick", this.handleHeaderClick, this);
52785
52786         if(grid.trackMouseOver){
52787             grid.on("mouseover", this.onRowOver, this);
52788             grid.on("mouseout", this.onRowOut, this);
52789         }
52790         grid.cancelTextSelection = function(){};
52791         this.gridId = grid.id;
52792
52793         var tpls = this.templates || {};
52794
52795         if(!tpls.master){
52796             tpls.master = new Roo.Template(
52797                '<div class="x-grid" hidefocus="true">',
52798                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52799                   '<div class="x-grid-topbar"></div>',
52800                   '<div class="x-grid-scroller"><div></div></div>',
52801                   '<div class="x-grid-locked">',
52802                       '<div class="x-grid-header">{lockedHeader}</div>',
52803                       '<div class="x-grid-body">{lockedBody}</div>',
52804                   "</div>",
52805                   '<div class="x-grid-viewport">',
52806                       '<div class="x-grid-header">{header}</div>',
52807                       '<div class="x-grid-body">{body}</div>',
52808                   "</div>",
52809                   '<div class="x-grid-bottombar"></div>',
52810                  
52811                   '<div class="x-grid-resize-proxy">&#160;</div>',
52812                "</div>"
52813             );
52814             tpls.master.disableformats = true;
52815         }
52816
52817         if(!tpls.header){
52818             tpls.header = new Roo.Template(
52819                '<table border="0" cellspacing="0" cellpadding="0">',
52820                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52821                "</table>{splits}"
52822             );
52823             tpls.header.disableformats = true;
52824         }
52825         tpls.header.compile();
52826
52827         if(!tpls.hcell){
52828             tpls.hcell = new Roo.Template(
52829                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52830                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52831                 "</div></td>"
52832              );
52833              tpls.hcell.disableFormats = true;
52834         }
52835         tpls.hcell.compile();
52836
52837         if(!tpls.hsplit){
52838             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52839                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52840             tpls.hsplit.disableFormats = true;
52841         }
52842         tpls.hsplit.compile();
52843
52844         if(!tpls.body){
52845             tpls.body = new Roo.Template(
52846                '<table border="0" cellspacing="0" cellpadding="0">',
52847                "<tbody>{rows}</tbody>",
52848                "</table>"
52849             );
52850             tpls.body.disableFormats = true;
52851         }
52852         tpls.body.compile();
52853
52854         if(!tpls.row){
52855             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52856             tpls.row.disableFormats = true;
52857         }
52858         tpls.row.compile();
52859
52860         if(!tpls.cell){
52861             tpls.cell = new Roo.Template(
52862                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52863                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52864                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52865                 "</td>"
52866             );
52867             tpls.cell.disableFormats = true;
52868         }
52869         tpls.cell.compile();
52870
52871         this.templates = tpls;
52872     },
52873
52874     // remap these for backwards compat
52875     onColWidthChange : function(){
52876         this.updateColumns.apply(this, arguments);
52877     },
52878     onHeaderChange : function(){
52879         this.updateHeaders.apply(this, arguments);
52880     }, 
52881     onHiddenChange : function(){
52882         this.handleHiddenChange.apply(this, arguments);
52883     },
52884     onColumnMove : function(){
52885         this.handleColumnMove.apply(this, arguments);
52886     },
52887     onColumnLock : function(){
52888         this.handleLockChange.apply(this, arguments);
52889     },
52890
52891     onDataChange : function(){
52892         this.refresh();
52893         this.updateHeaderSortState();
52894     },
52895
52896     onClear : function(){
52897         this.refresh();
52898     },
52899
52900     onUpdate : function(ds, record){
52901         this.refreshRow(record);
52902     },
52903
52904     refreshRow : function(record){
52905         var ds = this.ds, index;
52906         if(typeof record == 'number'){
52907             index = record;
52908             record = ds.getAt(index);
52909         }else{
52910             index = ds.indexOf(record);
52911         }
52912         this.insertRows(ds, index, index, true);
52913         this.onRemove(ds, record, index+1, true);
52914         this.syncRowHeights(index, index);
52915         this.layout();
52916         this.fireEvent("rowupdated", this, index, record);
52917     },
52918
52919     onAdd : function(ds, records, index){
52920         this.insertRows(ds, index, index + (records.length-1));
52921     },
52922
52923     onRemove : function(ds, record, index, isUpdate){
52924         if(isUpdate !== true){
52925             this.fireEvent("beforerowremoved", this, index, record);
52926         }
52927         var bt = this.getBodyTable(), lt = this.getLockedTable();
52928         if(bt.rows[index]){
52929             bt.firstChild.removeChild(bt.rows[index]);
52930         }
52931         if(lt.rows[index]){
52932             lt.firstChild.removeChild(lt.rows[index]);
52933         }
52934         if(isUpdate !== true){
52935             this.stripeRows(index);
52936             this.syncRowHeights(index, index);
52937             this.layout();
52938             this.fireEvent("rowremoved", this, index, record);
52939         }
52940     },
52941
52942     onLoad : function(){
52943         this.scrollToTop();
52944     },
52945
52946     /**
52947      * Scrolls the grid to the top
52948      */
52949     scrollToTop : function(){
52950         if(this.scroller){
52951             this.scroller.dom.scrollTop = 0;
52952             this.syncScroll();
52953         }
52954     },
52955
52956     /**
52957      * Gets a panel in the header of the grid that can be used for toolbars etc.
52958      * After modifying the contents of this panel a call to grid.autoSize() may be
52959      * required to register any changes in size.
52960      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52961      * @return Roo.Element
52962      */
52963     getHeaderPanel : function(doShow){
52964         if(doShow){
52965             this.headerPanel.show();
52966         }
52967         return this.headerPanel;
52968     },
52969
52970     /**
52971      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52972      * After modifying the contents of this panel a call to grid.autoSize() may be
52973      * required to register any changes in size.
52974      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52975      * @return Roo.Element
52976      */
52977     getFooterPanel : function(doShow){
52978         if(doShow){
52979             this.footerPanel.show();
52980         }
52981         return this.footerPanel;
52982     },
52983
52984     initElements : function(){
52985         var E = Roo.Element;
52986         var el = this.grid.getGridEl().dom.firstChild;
52987         var cs = el.childNodes;
52988
52989         this.el = new E(el);
52990         
52991          this.focusEl = new E(el.firstChild);
52992         this.focusEl.swallowEvent("click", true);
52993         
52994         this.headerPanel = new E(cs[1]);
52995         this.headerPanel.enableDisplayMode("block");
52996
52997         this.scroller = new E(cs[2]);
52998         this.scrollSizer = new E(this.scroller.dom.firstChild);
52999
53000         this.lockedWrap = new E(cs[3]);
53001         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53002         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53003
53004         this.mainWrap = new E(cs[4]);
53005         this.mainHd = new E(this.mainWrap.dom.firstChild);
53006         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53007
53008         this.footerPanel = new E(cs[5]);
53009         this.footerPanel.enableDisplayMode("block");
53010
53011         this.resizeProxy = new E(cs[6]);
53012
53013         this.headerSelector = String.format(
53014            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53015            this.lockedHd.id, this.mainHd.id
53016         );
53017
53018         this.splitterSelector = String.format(
53019            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53020            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53021         );
53022     },
53023     idToCssName : function(s)
53024     {
53025         return s.replace(/[^a-z0-9]+/ig, '-');
53026     },
53027
53028     getHeaderCell : function(index){
53029         return Roo.DomQuery.select(this.headerSelector)[index];
53030     },
53031
53032     getHeaderCellMeasure : function(index){
53033         return this.getHeaderCell(index).firstChild;
53034     },
53035
53036     getHeaderCellText : function(index){
53037         return this.getHeaderCell(index).firstChild.firstChild;
53038     },
53039
53040     getLockedTable : function(){
53041         return this.lockedBody.dom.firstChild;
53042     },
53043
53044     getBodyTable : function(){
53045         return this.mainBody.dom.firstChild;
53046     },
53047
53048     getLockedRow : function(index){
53049         return this.getLockedTable().rows[index];
53050     },
53051
53052     getRow : function(index){
53053         return this.getBodyTable().rows[index];
53054     },
53055
53056     getRowComposite : function(index){
53057         if(!this.rowEl){
53058             this.rowEl = new Roo.CompositeElementLite();
53059         }
53060         var els = [], lrow, mrow;
53061         if(lrow = this.getLockedRow(index)){
53062             els.push(lrow);
53063         }
53064         if(mrow = this.getRow(index)){
53065             els.push(mrow);
53066         }
53067         this.rowEl.elements = els;
53068         return this.rowEl;
53069     },
53070     /**
53071      * Gets the 'td' of the cell
53072      * 
53073      * @param {Integer} rowIndex row to select
53074      * @param {Integer} colIndex column to select
53075      * 
53076      * @return {Object} 
53077      */
53078     getCell : function(rowIndex, colIndex){
53079         var locked = this.cm.getLockedCount();
53080         var source;
53081         if(colIndex < locked){
53082             source = this.lockedBody.dom.firstChild;
53083         }else{
53084             source = this.mainBody.dom.firstChild;
53085             colIndex -= locked;
53086         }
53087         return source.rows[rowIndex].childNodes[colIndex];
53088     },
53089
53090     getCellText : function(rowIndex, colIndex){
53091         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53092     },
53093
53094     getCellBox : function(cell){
53095         var b = this.fly(cell).getBox();
53096         if(Roo.isOpera){ // opera fails to report the Y
53097             b.y = cell.offsetTop + this.mainBody.getY();
53098         }
53099         return b;
53100     },
53101
53102     getCellIndex : function(cell){
53103         var id = String(cell.className).match(this.cellRE);
53104         if(id){
53105             return parseInt(id[1], 10);
53106         }
53107         return 0;
53108     },
53109
53110     findHeaderIndex : function(n){
53111         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53112         return r ? this.getCellIndex(r) : false;
53113     },
53114
53115     findHeaderCell : function(n){
53116         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53117         return r ? r : false;
53118     },
53119
53120     findRowIndex : function(n){
53121         if(!n){
53122             return false;
53123         }
53124         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53125         return r ? r.rowIndex : false;
53126     },
53127
53128     findCellIndex : function(node){
53129         var stop = this.el.dom;
53130         while(node && node != stop){
53131             if(this.findRE.test(node.className)){
53132                 return this.getCellIndex(node);
53133             }
53134             node = node.parentNode;
53135         }
53136         return false;
53137     },
53138
53139     getColumnId : function(index){
53140         return this.cm.getColumnId(index);
53141     },
53142
53143     getSplitters : function()
53144     {
53145         if(this.splitterSelector){
53146            return Roo.DomQuery.select(this.splitterSelector);
53147         }else{
53148             return null;
53149       }
53150     },
53151
53152     getSplitter : function(index){
53153         return this.getSplitters()[index];
53154     },
53155
53156     onRowOver : function(e, t){
53157         var row;
53158         if((row = this.findRowIndex(t)) !== false){
53159             this.getRowComposite(row).addClass("x-grid-row-over");
53160         }
53161     },
53162
53163     onRowOut : function(e, t){
53164         var row;
53165         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53166             this.getRowComposite(row).removeClass("x-grid-row-over");
53167         }
53168     },
53169
53170     renderHeaders : function(){
53171         var cm = this.cm;
53172         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53173         var cb = [], lb = [], sb = [], lsb = [], p = {};
53174         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53175             p.cellId = "x-grid-hd-0-" + i;
53176             p.splitId = "x-grid-csplit-0-" + i;
53177             p.id = cm.getColumnId(i);
53178             p.title = cm.getColumnTooltip(i) || "";
53179             p.value = cm.getColumnHeader(i) || "";
53180             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53181             if(!cm.isLocked(i)){
53182                 cb[cb.length] = ct.apply(p);
53183                 sb[sb.length] = st.apply(p);
53184             }else{
53185                 lb[lb.length] = ct.apply(p);
53186                 lsb[lsb.length] = st.apply(p);
53187             }
53188         }
53189         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53190                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53191     },
53192
53193     updateHeaders : function(){
53194         var html = this.renderHeaders();
53195         this.lockedHd.update(html[0]);
53196         this.mainHd.update(html[1]);
53197     },
53198
53199     /**
53200      * Focuses the specified row.
53201      * @param {Number} row The row index
53202      */
53203     focusRow : function(row)
53204     {
53205         //Roo.log('GridView.focusRow');
53206         var x = this.scroller.dom.scrollLeft;
53207         this.focusCell(row, 0, false);
53208         this.scroller.dom.scrollLeft = x;
53209     },
53210
53211     /**
53212      * Focuses the specified cell.
53213      * @param {Number} row The row index
53214      * @param {Number} col The column index
53215      * @param {Boolean} hscroll false to disable horizontal scrolling
53216      */
53217     focusCell : function(row, col, hscroll)
53218     {
53219         //Roo.log('GridView.focusCell');
53220         var el = this.ensureVisible(row, col, hscroll);
53221         this.focusEl.alignTo(el, "tl-tl");
53222         if(Roo.isGecko){
53223             this.focusEl.focus();
53224         }else{
53225             this.focusEl.focus.defer(1, this.focusEl);
53226         }
53227     },
53228
53229     /**
53230      * Scrolls the specified cell into view
53231      * @param {Number} row The row index
53232      * @param {Number} col The column index
53233      * @param {Boolean} hscroll false to disable horizontal scrolling
53234      */
53235     ensureVisible : function(row, col, hscroll)
53236     {
53237         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53238         //return null; //disable for testing.
53239         if(typeof row != "number"){
53240             row = row.rowIndex;
53241         }
53242         if(row < 0 && row >= this.ds.getCount()){
53243             return  null;
53244         }
53245         col = (col !== undefined ? col : 0);
53246         var cm = this.grid.colModel;
53247         while(cm.isHidden(col)){
53248             col++;
53249         }
53250
53251         var el = this.getCell(row, col);
53252         if(!el){
53253             return null;
53254         }
53255         var c = this.scroller.dom;
53256
53257         var ctop = parseInt(el.offsetTop, 10);
53258         var cleft = parseInt(el.offsetLeft, 10);
53259         var cbot = ctop + el.offsetHeight;
53260         var cright = cleft + el.offsetWidth;
53261         
53262         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53263         var stop = parseInt(c.scrollTop, 10);
53264         var sleft = parseInt(c.scrollLeft, 10);
53265         var sbot = stop + ch;
53266         var sright = sleft + c.clientWidth;
53267         /*
53268         Roo.log('GridView.ensureVisible:' +
53269                 ' ctop:' + ctop +
53270                 ' c.clientHeight:' + c.clientHeight +
53271                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53272                 ' stop:' + stop +
53273                 ' cbot:' + cbot +
53274                 ' sbot:' + sbot +
53275                 ' ch:' + ch  
53276                 );
53277         */
53278         if(ctop < stop){
53279              c.scrollTop = ctop;
53280             //Roo.log("set scrolltop to ctop DISABLE?");
53281         }else if(cbot > sbot){
53282             //Roo.log("set scrolltop to cbot-ch");
53283             c.scrollTop = cbot-ch;
53284         }
53285         
53286         if(hscroll !== false){
53287             if(cleft < sleft){
53288                 c.scrollLeft = cleft;
53289             }else if(cright > sright){
53290                 c.scrollLeft = cright-c.clientWidth;
53291             }
53292         }
53293          
53294         return el;
53295     },
53296
53297     updateColumns : function(){
53298         this.grid.stopEditing();
53299         var cm = this.grid.colModel, colIds = this.getColumnIds();
53300         //var totalWidth = cm.getTotalWidth();
53301         var pos = 0;
53302         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53303             //if(cm.isHidden(i)) continue;
53304             var w = cm.getColumnWidth(i);
53305             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53306             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53307         }
53308         this.updateSplitters();
53309     },
53310
53311     generateRules : function(cm){
53312         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53313         Roo.util.CSS.removeStyleSheet(rulesId);
53314         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53315             var cid = cm.getColumnId(i);
53316             var align = '';
53317             if(cm.config[i].align){
53318                 align = 'text-align:'+cm.config[i].align+';';
53319             }
53320             var hidden = '';
53321             if(cm.isHidden(i)){
53322                 hidden = 'display:none;';
53323             }
53324             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53325             ruleBuf.push(
53326                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53327                     this.hdSelector, cid, " {\n", align, width, "}\n",
53328                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53329                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53330         }
53331         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53332     },
53333
53334     updateSplitters : function(){
53335         var cm = this.cm, s = this.getSplitters();
53336         if(s){ // splitters not created yet
53337             var pos = 0, locked = true;
53338             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53339                 if(cm.isHidden(i)) continue;
53340                 var w = cm.getColumnWidth(i); // make sure it's a number
53341                 if(!cm.isLocked(i) && locked){
53342                     pos = 0;
53343                     locked = false;
53344                 }
53345                 pos += w;
53346                 s[i].style.left = (pos-this.splitOffset) + "px";
53347             }
53348         }
53349     },
53350
53351     handleHiddenChange : function(colModel, colIndex, hidden){
53352         if(hidden){
53353             this.hideColumn(colIndex);
53354         }else{
53355             this.unhideColumn(colIndex);
53356         }
53357     },
53358
53359     hideColumn : function(colIndex){
53360         var cid = this.getColumnId(colIndex);
53361         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53362         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53363         if(Roo.isSafari){
53364             this.updateHeaders();
53365         }
53366         this.updateSplitters();
53367         this.layout();
53368     },
53369
53370     unhideColumn : function(colIndex){
53371         var cid = this.getColumnId(colIndex);
53372         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53373         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53374
53375         if(Roo.isSafari){
53376             this.updateHeaders();
53377         }
53378         this.updateSplitters();
53379         this.layout();
53380     },
53381
53382     insertRows : function(dm, firstRow, lastRow, isUpdate){
53383         if(firstRow == 0 && lastRow == dm.getCount()-1){
53384             this.refresh();
53385         }else{
53386             if(!isUpdate){
53387                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53388             }
53389             var s = this.getScrollState();
53390             var markup = this.renderRows(firstRow, lastRow);
53391             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53392             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53393             this.restoreScroll(s);
53394             if(!isUpdate){
53395                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53396                 this.syncRowHeights(firstRow, lastRow);
53397                 this.stripeRows(firstRow);
53398                 this.layout();
53399             }
53400         }
53401     },
53402
53403     bufferRows : function(markup, target, index){
53404         var before = null, trows = target.rows, tbody = target.tBodies[0];
53405         if(index < trows.length){
53406             before = trows[index];
53407         }
53408         var b = document.createElement("div");
53409         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53410         var rows = b.firstChild.rows;
53411         for(var i = 0, len = rows.length; i < len; i++){
53412             if(before){
53413                 tbody.insertBefore(rows[0], before);
53414             }else{
53415                 tbody.appendChild(rows[0]);
53416             }
53417         }
53418         b.innerHTML = "";
53419         b = null;
53420     },
53421
53422     deleteRows : function(dm, firstRow, lastRow){
53423         if(dm.getRowCount()<1){
53424             this.fireEvent("beforerefresh", this);
53425             this.mainBody.update("");
53426             this.lockedBody.update("");
53427             this.fireEvent("refresh", this);
53428         }else{
53429             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53430             var bt = this.getBodyTable();
53431             var tbody = bt.firstChild;
53432             var rows = bt.rows;
53433             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53434                 tbody.removeChild(rows[firstRow]);
53435             }
53436             this.stripeRows(firstRow);
53437             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53438         }
53439     },
53440
53441     updateRows : function(dataSource, firstRow, lastRow){
53442         var s = this.getScrollState();
53443         this.refresh();
53444         this.restoreScroll(s);
53445     },
53446
53447     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53448         if(!noRefresh){
53449            this.refresh();
53450         }
53451         this.updateHeaderSortState();
53452     },
53453
53454     getScrollState : function(){
53455         
53456         var sb = this.scroller.dom;
53457         return {left: sb.scrollLeft, top: sb.scrollTop};
53458     },
53459
53460     stripeRows : function(startRow){
53461         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53462             return;
53463         }
53464         startRow = startRow || 0;
53465         var rows = this.getBodyTable().rows;
53466         var lrows = this.getLockedTable().rows;
53467         var cls = ' x-grid-row-alt ';
53468         for(var i = startRow, len = rows.length; i < len; i++){
53469             var row = rows[i], lrow = lrows[i];
53470             var isAlt = ((i+1) % 2 == 0);
53471             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53472             if(isAlt == hasAlt){
53473                 continue;
53474             }
53475             if(isAlt){
53476                 row.className += " x-grid-row-alt";
53477             }else{
53478                 row.className = row.className.replace("x-grid-row-alt", "");
53479             }
53480             if(lrow){
53481                 lrow.className = row.className;
53482             }
53483         }
53484     },
53485
53486     restoreScroll : function(state){
53487         //Roo.log('GridView.restoreScroll');
53488         var sb = this.scroller.dom;
53489         sb.scrollLeft = state.left;
53490         sb.scrollTop = state.top;
53491         this.syncScroll();
53492     },
53493
53494     syncScroll : function(){
53495         //Roo.log('GridView.syncScroll');
53496         var sb = this.scroller.dom;
53497         var sh = this.mainHd.dom;
53498         var bs = this.mainBody.dom;
53499         var lv = this.lockedBody.dom;
53500         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53501         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53502     },
53503
53504     handleScroll : function(e){
53505         this.syncScroll();
53506         var sb = this.scroller.dom;
53507         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53508         e.stopEvent();
53509     },
53510
53511     handleWheel : function(e){
53512         var d = e.getWheelDelta();
53513         this.scroller.dom.scrollTop -= d*22;
53514         // set this here to prevent jumpy scrolling on large tables
53515         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53516         e.stopEvent();
53517     },
53518
53519     renderRows : function(startRow, endRow){
53520         // pull in all the crap needed to render rows
53521         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53522         var colCount = cm.getColumnCount();
53523
53524         if(ds.getCount() < 1){
53525             return ["", ""];
53526         }
53527
53528         // build a map for all the columns
53529         var cs = [];
53530         for(var i = 0; i < colCount; i++){
53531             var name = cm.getDataIndex(i);
53532             cs[i] = {
53533                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53534                 renderer : cm.getRenderer(i),
53535                 id : cm.getColumnId(i),
53536                 locked : cm.isLocked(i)
53537             };
53538         }
53539
53540         startRow = startRow || 0;
53541         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53542
53543         // records to render
53544         var rs = ds.getRange(startRow, endRow);
53545
53546         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53547     },
53548
53549     // As much as I hate to duplicate code, this was branched because FireFox really hates
53550     // [].join("") on strings. The performance difference was substantial enough to
53551     // branch this function
53552     doRender : Roo.isGecko ?
53553             function(cs, rs, ds, startRow, colCount, stripe){
53554                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53555                 // buffers
53556                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53557                 
53558                 var hasListener = this.grid.hasListener('rowclass');
53559                 var rowcfg = {};
53560                 for(var j = 0, len = rs.length; j < len; j++){
53561                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53562                     for(var i = 0; i < colCount; i++){
53563                         c = cs[i];
53564                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53565                         p.id = c.id;
53566                         p.css = p.attr = "";
53567                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53568                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53569                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53570                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53571                         }
53572                         var markup = ct.apply(p);
53573                         if(!c.locked){
53574                             cb+= markup;
53575                         }else{
53576                             lcb+= markup;
53577                         }
53578                     }
53579                     var alt = [];
53580                     if(stripe && ((rowIndex+1) % 2 == 0)){
53581                         alt.push("x-grid-row-alt")
53582                     }
53583                     if(r.dirty){
53584                         alt.push(  " x-grid-dirty-row");
53585                     }
53586                     rp.cells = lcb;
53587                     if(this.getRowClass){
53588                         alt.push(this.getRowClass(r, rowIndex));
53589                     }
53590                     if (hasListener) {
53591                         rowcfg = {
53592                              
53593                             record: r,
53594                             rowIndex : rowIndex,
53595                             rowClass : ''
53596                         }
53597                         this.grid.fireEvent('rowclass', this, rowcfg);
53598                         alt.push(rowcfg.rowClass);
53599                     }
53600                     rp.alt = alt.join(" ");
53601                     lbuf+= rt.apply(rp);
53602                     rp.cells = cb;
53603                     buf+=  rt.apply(rp);
53604                 }
53605                 return [lbuf, buf];
53606             } :
53607             function(cs, rs, ds, startRow, colCount, stripe){
53608                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53609                 // buffers
53610                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53611                 var hasListener = this.grid.hasListener('rowclass');
53612  
53613                 var rowcfg = {};
53614                 for(var j = 0, len = rs.length; j < len; j++){
53615                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53616                     for(var i = 0; i < colCount; i++){
53617                         c = cs[i];
53618                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53619                         p.id = c.id;
53620                         p.css = p.attr = "";
53621                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53622                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53623                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53624                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53625                         }
53626                         
53627                         var markup = ct.apply(p);
53628                         if(!c.locked){
53629                             cb[cb.length] = markup;
53630                         }else{
53631                             lcb[lcb.length] = markup;
53632                         }
53633                     }
53634                     var alt = [];
53635                     if(stripe && ((rowIndex+1) % 2 == 0)){
53636                         alt.push( "x-grid-row-alt");
53637                     }
53638                     if(r.dirty){
53639                         alt.push(" x-grid-dirty-row");
53640                     }
53641                     rp.cells = lcb;
53642                     if(this.getRowClass){
53643                         alt.push( this.getRowClass(r, rowIndex));
53644                     }
53645                     if (hasListener) {
53646                         rowcfg = {
53647                              
53648                             record: r,
53649                             rowIndex : rowIndex,
53650                             rowClass : ''
53651                         }
53652                         this.grid.fireEvent('rowclass', this, rowcfg);
53653                         alt.push(rowcfg.rowClass);
53654                     }
53655                     rp.alt = alt.join(" ");
53656                     rp.cells = lcb.join("");
53657                     lbuf[lbuf.length] = rt.apply(rp);
53658                     rp.cells = cb.join("");
53659                     buf[buf.length] =  rt.apply(rp);
53660                 }
53661                 return [lbuf.join(""), buf.join("")];
53662             },
53663
53664     renderBody : function(){
53665         var markup = this.renderRows();
53666         var bt = this.templates.body;
53667         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53668     },
53669
53670     /**
53671      * Refreshes the grid
53672      * @param {Boolean} headersToo
53673      */
53674     refresh : function(headersToo){
53675         this.fireEvent("beforerefresh", this);
53676         this.grid.stopEditing();
53677         var result = this.renderBody();
53678         this.lockedBody.update(result[0]);
53679         this.mainBody.update(result[1]);
53680         if(headersToo === true){
53681             this.updateHeaders();
53682             this.updateColumns();
53683             this.updateSplitters();
53684             this.updateHeaderSortState();
53685         }
53686         this.syncRowHeights();
53687         this.layout();
53688         this.fireEvent("refresh", this);
53689     },
53690
53691     handleColumnMove : function(cm, oldIndex, newIndex){
53692         this.indexMap = null;
53693         var s = this.getScrollState();
53694         this.refresh(true);
53695         this.restoreScroll(s);
53696         this.afterMove(newIndex);
53697     },
53698
53699     afterMove : function(colIndex){
53700         if(this.enableMoveAnim && Roo.enableFx){
53701             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53702         }
53703         // if multisort - fix sortOrder, and reload..
53704         if (this.grid.dataSource.multiSort) {
53705             // the we can call sort again..
53706             var dm = this.grid.dataSource;
53707             var cm = this.grid.colModel;
53708             var so = [];
53709             for(var i = 0; i < cm.config.length; i++ ) {
53710                 
53711                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53712                     continue; // dont' bother, it's not in sort list or being set.
53713                 }
53714                 
53715                 so.push(cm.config[i].dataIndex);
53716             };
53717             dm.sortOrder = so;
53718             dm.load(dm.lastOptions);
53719             
53720             
53721         }
53722         
53723     },
53724
53725     updateCell : function(dm, rowIndex, dataIndex){
53726         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53727         if(typeof colIndex == "undefined"){ // not present in grid
53728             return;
53729         }
53730         var cm = this.grid.colModel;
53731         var cell = this.getCell(rowIndex, colIndex);
53732         var cellText = this.getCellText(rowIndex, colIndex);
53733
53734         var p = {
53735             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53736             id : cm.getColumnId(colIndex),
53737             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53738         };
53739         var renderer = cm.getRenderer(colIndex);
53740         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53741         if(typeof val == "undefined" || val === "") val = "&#160;";
53742         cellText.innerHTML = val;
53743         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53744         this.syncRowHeights(rowIndex, rowIndex);
53745     },
53746
53747     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53748         var maxWidth = 0;
53749         if(this.grid.autoSizeHeaders){
53750             var h = this.getHeaderCellMeasure(colIndex);
53751             maxWidth = Math.max(maxWidth, h.scrollWidth);
53752         }
53753         var tb, index;
53754         if(this.cm.isLocked(colIndex)){
53755             tb = this.getLockedTable();
53756             index = colIndex;
53757         }else{
53758             tb = this.getBodyTable();
53759             index = colIndex - this.cm.getLockedCount();
53760         }
53761         if(tb && tb.rows){
53762             var rows = tb.rows;
53763             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53764             for(var i = 0; i < stopIndex; i++){
53765                 var cell = rows[i].childNodes[index].firstChild;
53766                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53767             }
53768         }
53769         return maxWidth + /*margin for error in IE*/ 5;
53770     },
53771     /**
53772      * Autofit a column to its content.
53773      * @param {Number} colIndex
53774      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53775      */
53776      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53777          if(this.cm.isHidden(colIndex)){
53778              return; // can't calc a hidden column
53779          }
53780         if(forceMinSize){
53781             var cid = this.cm.getColumnId(colIndex);
53782             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53783            if(this.grid.autoSizeHeaders){
53784                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53785            }
53786         }
53787         var newWidth = this.calcColumnWidth(colIndex);
53788         this.cm.setColumnWidth(colIndex,
53789             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53790         if(!suppressEvent){
53791             this.grid.fireEvent("columnresize", colIndex, newWidth);
53792         }
53793     },
53794
53795     /**
53796      * Autofits all columns to their content and then expands to fit any extra space in the grid
53797      */
53798      autoSizeColumns : function(){
53799         var cm = this.grid.colModel;
53800         var colCount = cm.getColumnCount();
53801         for(var i = 0; i < colCount; i++){
53802             this.autoSizeColumn(i, true, true);
53803         }
53804         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53805             this.fitColumns();
53806         }else{
53807             this.updateColumns();
53808             this.layout();
53809         }
53810     },
53811
53812     /**
53813      * Autofits all columns to the grid's width proportionate with their current size
53814      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53815      */
53816     fitColumns : function(reserveScrollSpace){
53817         var cm = this.grid.colModel;
53818         var colCount = cm.getColumnCount();
53819         var cols = [];
53820         var width = 0;
53821         var i, w;
53822         for (i = 0; i < colCount; i++){
53823             if(!cm.isHidden(i) && !cm.isFixed(i)){
53824                 w = cm.getColumnWidth(i);
53825                 cols.push(i);
53826                 cols.push(w);
53827                 width += w;
53828             }
53829         }
53830         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53831         if(reserveScrollSpace){
53832             avail -= 17;
53833         }
53834         var frac = (avail - cm.getTotalWidth())/width;
53835         while (cols.length){
53836             w = cols.pop();
53837             i = cols.pop();
53838             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53839         }
53840         this.updateColumns();
53841         this.layout();
53842     },
53843
53844     onRowSelect : function(rowIndex){
53845         var row = this.getRowComposite(rowIndex);
53846         row.addClass("x-grid-row-selected");
53847     },
53848
53849     onRowDeselect : function(rowIndex){
53850         var row = this.getRowComposite(rowIndex);
53851         row.removeClass("x-grid-row-selected");
53852     },
53853
53854     onCellSelect : function(row, col){
53855         var cell = this.getCell(row, col);
53856         if(cell){
53857             Roo.fly(cell).addClass("x-grid-cell-selected");
53858         }
53859     },
53860
53861     onCellDeselect : function(row, col){
53862         var cell = this.getCell(row, col);
53863         if(cell){
53864             Roo.fly(cell).removeClass("x-grid-cell-selected");
53865         }
53866     },
53867
53868     updateHeaderSortState : function(){
53869         
53870         // sort state can be single { field: xxx, direction : yyy}
53871         // or   { xxx=>ASC , yyy : DESC ..... }
53872         
53873         var mstate = {};
53874         if (!this.ds.multiSort) { 
53875             var state = this.ds.getSortState();
53876             if(!state){
53877                 return;
53878             }
53879             mstate[state.field] = state.direction;
53880             // FIXME... - this is not used here.. but might be elsewhere..
53881             this.sortState = state;
53882             
53883         } else {
53884             mstate = this.ds.sortToggle;
53885         }
53886         //remove existing sort classes..
53887         
53888         var sc = this.sortClasses;
53889         var hds = this.el.select(this.headerSelector).removeClass(sc);
53890         
53891         for(var f in mstate) {
53892         
53893             var sortColumn = this.cm.findColumnIndex(f);
53894             
53895             if(sortColumn != -1){
53896                 var sortDir = mstate[f];        
53897                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53898             }
53899         }
53900         
53901          
53902         
53903     },
53904
53905
53906     handleHeaderClick : function(g, index,e){
53907         
53908         Roo.log("header click");
53909         
53910         if (Roo.isTouch) {
53911             // touch events on header are handled by context
53912             this.handleHdCtx(g,index,e);
53913             return;
53914         }
53915         
53916         
53917         if(this.headersDisabled){
53918             return;
53919         }
53920         var dm = g.dataSource, cm = g.colModel;
53921         if(!cm.isSortable(index)){
53922             return;
53923         }
53924         g.stopEditing();
53925         
53926         if (dm.multiSort) {
53927             // update the sortOrder
53928             var so = [];
53929             for(var i = 0; i < cm.config.length; i++ ) {
53930                 
53931                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53932                     continue; // dont' bother, it's not in sort list or being set.
53933                 }
53934                 
53935                 so.push(cm.config[i].dataIndex);
53936             };
53937             dm.sortOrder = so;
53938         }
53939         
53940         
53941         dm.sort(cm.getDataIndex(index));
53942     },
53943
53944
53945     destroy : function(){
53946         if(this.colMenu){
53947             this.colMenu.removeAll();
53948             Roo.menu.MenuMgr.unregister(this.colMenu);
53949             this.colMenu.getEl().remove();
53950             delete this.colMenu;
53951         }
53952         if(this.hmenu){
53953             this.hmenu.removeAll();
53954             Roo.menu.MenuMgr.unregister(this.hmenu);
53955             this.hmenu.getEl().remove();
53956             delete this.hmenu;
53957         }
53958         if(this.grid.enableColumnMove){
53959             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53960             if(dds){
53961                 for(var dd in dds){
53962                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53963                         var elid = dds[dd].dragElId;
53964                         dds[dd].unreg();
53965                         Roo.get(elid).remove();
53966                     } else if(dds[dd].config.isTarget){
53967                         dds[dd].proxyTop.remove();
53968                         dds[dd].proxyBottom.remove();
53969                         dds[dd].unreg();
53970                     }
53971                     if(Roo.dd.DDM.locationCache[dd]){
53972                         delete Roo.dd.DDM.locationCache[dd];
53973                     }
53974                 }
53975                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53976             }
53977         }
53978         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53979         this.bind(null, null);
53980         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53981     },
53982
53983     handleLockChange : function(){
53984         this.refresh(true);
53985     },
53986
53987     onDenyColumnLock : function(){
53988
53989     },
53990
53991     onDenyColumnHide : function(){
53992
53993     },
53994
53995     handleHdMenuClick : function(item){
53996         var index = this.hdCtxIndex;
53997         var cm = this.cm, ds = this.ds;
53998         switch(item.id){
53999             case "asc":
54000                 ds.sort(cm.getDataIndex(index), "ASC");
54001                 break;
54002             case "desc":
54003                 ds.sort(cm.getDataIndex(index), "DESC");
54004                 break;
54005             case "lock":
54006                 var lc = cm.getLockedCount();
54007                 if(cm.getColumnCount(true) <= lc+1){
54008                     this.onDenyColumnLock();
54009                     return;
54010                 }
54011                 if(lc != index){
54012                     cm.setLocked(index, true, true);
54013                     cm.moveColumn(index, lc);
54014                     this.grid.fireEvent("columnmove", index, lc);
54015                 }else{
54016                     cm.setLocked(index, true);
54017                 }
54018             break;
54019             case "unlock":
54020                 var lc = cm.getLockedCount();
54021                 if((lc-1) != index){
54022                     cm.setLocked(index, false, true);
54023                     cm.moveColumn(index, lc-1);
54024                     this.grid.fireEvent("columnmove", index, lc-1);
54025                 }else{
54026                     cm.setLocked(index, false);
54027                 }
54028             break;
54029             case 'wider': // used to expand cols on touch..
54030             case 'narrow':
54031                 var cw = cm.getColumnWidth(index);
54032                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54033                 cw = Math.max(0, cw);
54034                 cw = Math.min(cw,4000);
54035                 cm.setColumnWidth(index, cw);
54036                 break;
54037                 
54038             default:
54039                 index = cm.getIndexById(item.id.substr(4));
54040                 if(index != -1){
54041                     if(item.checked && cm.getColumnCount(true) <= 1){
54042                         this.onDenyColumnHide();
54043                         return false;
54044                     }
54045                     cm.setHidden(index, item.checked);
54046                 }
54047         }
54048         return true;
54049     },
54050
54051     beforeColMenuShow : function(){
54052         var cm = this.cm,  colCount = cm.getColumnCount();
54053         this.colMenu.removeAll();
54054         for(var i = 0; i < colCount; i++){
54055             this.colMenu.add(new Roo.menu.CheckItem({
54056                 id: "col-"+cm.getColumnId(i),
54057                 text: cm.getColumnHeader(i),
54058                 checked: !cm.isHidden(i),
54059                 hideOnClick:false
54060             }));
54061         }
54062     },
54063
54064     handleHdCtx : function(g, index, e){
54065         e.stopEvent();
54066         var hd = this.getHeaderCell(index);
54067         this.hdCtxIndex = index;
54068         var ms = this.hmenu.items, cm = this.cm;
54069         ms.get("asc").setDisabled(!cm.isSortable(index));
54070         ms.get("desc").setDisabled(!cm.isSortable(index));
54071         if(this.grid.enableColLock !== false){
54072             ms.get("lock").setDisabled(cm.isLocked(index));
54073             ms.get("unlock").setDisabled(!cm.isLocked(index));
54074         }
54075         this.hmenu.show(hd, "tl-bl");
54076     },
54077
54078     handleHdOver : function(e){
54079         var hd = this.findHeaderCell(e.getTarget());
54080         if(hd && !this.headersDisabled){
54081             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54082                this.fly(hd).addClass("x-grid-hd-over");
54083             }
54084         }
54085     },
54086
54087     handleHdOut : function(e){
54088         var hd = this.findHeaderCell(e.getTarget());
54089         if(hd){
54090             this.fly(hd).removeClass("x-grid-hd-over");
54091         }
54092     },
54093
54094     handleSplitDblClick : function(e, t){
54095         var i = this.getCellIndex(t);
54096         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54097             this.autoSizeColumn(i, true);
54098             this.layout();
54099         }
54100     },
54101
54102     render : function(){
54103
54104         var cm = this.cm;
54105         var colCount = cm.getColumnCount();
54106
54107         if(this.grid.monitorWindowResize === true){
54108             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54109         }
54110         var header = this.renderHeaders();
54111         var body = this.templates.body.apply({rows:""});
54112         var html = this.templates.master.apply({
54113             lockedBody: body,
54114             body: body,
54115             lockedHeader: header[0],
54116             header: header[1]
54117         });
54118
54119         //this.updateColumns();
54120
54121         this.grid.getGridEl().dom.innerHTML = html;
54122
54123         this.initElements();
54124         
54125         // a kludge to fix the random scolling effect in webkit
54126         this.el.on("scroll", function() {
54127             this.el.dom.scrollTop=0; // hopefully not recursive..
54128         },this);
54129
54130         this.scroller.on("scroll", this.handleScroll, this);
54131         this.lockedBody.on("mousewheel", this.handleWheel, this);
54132         this.mainBody.on("mousewheel", this.handleWheel, this);
54133
54134         this.mainHd.on("mouseover", this.handleHdOver, this);
54135         this.mainHd.on("mouseout", this.handleHdOut, this);
54136         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54137                 {delegate: "."+this.splitClass});
54138
54139         this.lockedHd.on("mouseover", this.handleHdOver, this);
54140         this.lockedHd.on("mouseout", this.handleHdOut, this);
54141         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54142                 {delegate: "."+this.splitClass});
54143
54144         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54145             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54146         }
54147
54148         this.updateSplitters();
54149
54150         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54151             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54152             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54153         }
54154
54155         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54156             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54157             this.hmenu.add(
54158                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54159                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54160             );
54161             if(this.grid.enableColLock !== false){
54162                 this.hmenu.add('-',
54163                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54164                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54165                 );
54166             }
54167             if (Roo.isTouch) {
54168                  this.hmenu.add('-',
54169                     {id:"wider", text: this.columnsWiderText},
54170                     {id:"narrow", text: this.columnsNarrowText }
54171                 );
54172                 
54173                  
54174             }
54175             
54176             if(this.grid.enableColumnHide !== false){
54177
54178                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54179                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54180                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54181
54182                 this.hmenu.add('-',
54183                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54184                 );
54185             }
54186             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54187
54188             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54189         }
54190
54191         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54192             this.dd = new Roo.grid.GridDragZone(this.grid, {
54193                 ddGroup : this.grid.ddGroup || 'GridDD'
54194             });
54195             
54196         }
54197
54198         /*
54199         for(var i = 0; i < colCount; i++){
54200             if(cm.isHidden(i)){
54201                 this.hideColumn(i);
54202             }
54203             if(cm.config[i].align){
54204                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54205                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54206             }
54207         }*/
54208         
54209         this.updateHeaderSortState();
54210
54211         this.beforeInitialResize();
54212         this.layout(true);
54213
54214         // two part rendering gives faster view to the user
54215         this.renderPhase2.defer(1, this);
54216     },
54217
54218     renderPhase2 : function(){
54219         // render the rows now
54220         this.refresh();
54221         if(this.grid.autoSizeColumns){
54222             this.autoSizeColumns();
54223         }
54224     },
54225
54226     beforeInitialResize : function(){
54227
54228     },
54229
54230     onColumnSplitterMoved : function(i, w){
54231         this.userResized = true;
54232         var cm = this.grid.colModel;
54233         cm.setColumnWidth(i, w, true);
54234         var cid = cm.getColumnId(i);
54235         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54236         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54237         this.updateSplitters();
54238         this.layout();
54239         this.grid.fireEvent("columnresize", i, w);
54240     },
54241
54242     syncRowHeights : function(startIndex, endIndex){
54243         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54244             startIndex = startIndex || 0;
54245             var mrows = this.getBodyTable().rows;
54246             var lrows = this.getLockedTable().rows;
54247             var len = mrows.length-1;
54248             endIndex = Math.min(endIndex || len, len);
54249             for(var i = startIndex; i <= endIndex; i++){
54250                 var m = mrows[i], l = lrows[i];
54251                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54252                 m.style.height = l.style.height = h + "px";
54253             }
54254         }
54255     },
54256
54257     layout : function(initialRender, is2ndPass){
54258         var g = this.grid;
54259         var auto = g.autoHeight;
54260         var scrollOffset = 16;
54261         var c = g.getGridEl(), cm = this.cm,
54262                 expandCol = g.autoExpandColumn,
54263                 gv = this;
54264         //c.beginMeasure();
54265
54266         if(!c.dom.offsetWidth){ // display:none?
54267             if(initialRender){
54268                 this.lockedWrap.show();
54269                 this.mainWrap.show();
54270             }
54271             return;
54272         }
54273
54274         var hasLock = this.cm.isLocked(0);
54275
54276         var tbh = this.headerPanel.getHeight();
54277         var bbh = this.footerPanel.getHeight();
54278
54279         if(auto){
54280             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54281             var newHeight = ch + c.getBorderWidth("tb");
54282             if(g.maxHeight){
54283                 newHeight = Math.min(g.maxHeight, newHeight);
54284             }
54285             c.setHeight(newHeight);
54286         }
54287
54288         if(g.autoWidth){
54289             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54290         }
54291
54292         var s = this.scroller;
54293
54294         var csize = c.getSize(true);
54295
54296         this.el.setSize(csize.width, csize.height);
54297
54298         this.headerPanel.setWidth(csize.width);
54299         this.footerPanel.setWidth(csize.width);
54300
54301         var hdHeight = this.mainHd.getHeight();
54302         var vw = csize.width;
54303         var vh = csize.height - (tbh + bbh);
54304
54305         s.setSize(vw, vh);
54306
54307         var bt = this.getBodyTable();
54308         var ltWidth = hasLock ?
54309                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54310
54311         var scrollHeight = bt.offsetHeight;
54312         var scrollWidth = ltWidth + bt.offsetWidth;
54313         var vscroll = false, hscroll = false;
54314
54315         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54316
54317         var lw = this.lockedWrap, mw = this.mainWrap;
54318         var lb = this.lockedBody, mb = this.mainBody;
54319
54320         setTimeout(function(){
54321             var t = s.dom.offsetTop;
54322             var w = s.dom.clientWidth,
54323                 h = s.dom.clientHeight;
54324
54325             lw.setTop(t);
54326             lw.setSize(ltWidth, h);
54327
54328             mw.setLeftTop(ltWidth, t);
54329             mw.setSize(w-ltWidth, h);
54330
54331             lb.setHeight(h-hdHeight);
54332             mb.setHeight(h-hdHeight);
54333
54334             if(is2ndPass !== true && !gv.userResized && expandCol){
54335                 // high speed resize without full column calculation
54336                 
54337                 var ci = cm.getIndexById(expandCol);
54338                 if (ci < 0) {
54339                     ci = cm.findColumnIndex(expandCol);
54340                 }
54341                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54342                 var expandId = cm.getColumnId(ci);
54343                 var  tw = cm.getTotalWidth(false);
54344                 var currentWidth = cm.getColumnWidth(ci);
54345                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54346                 if(currentWidth != cw){
54347                     cm.setColumnWidth(ci, cw, true);
54348                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54349                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54350                     gv.updateSplitters();
54351                     gv.layout(false, true);
54352                 }
54353             }
54354
54355             if(initialRender){
54356                 lw.show();
54357                 mw.show();
54358             }
54359             //c.endMeasure();
54360         }, 10);
54361     },
54362
54363     onWindowResize : function(){
54364         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54365             return;
54366         }
54367         this.layout();
54368     },
54369
54370     appendFooter : function(parentEl){
54371         return null;
54372     },
54373
54374     sortAscText : "Sort Ascending",
54375     sortDescText : "Sort Descending",
54376     lockText : "Lock Column",
54377     unlockText : "Unlock Column",
54378     columnsText : "Columns",
54379  
54380     columnsWiderText : "Wider",
54381     columnsNarrowText : "Thinner"
54382 });
54383
54384
54385 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54386     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54387     this.proxy.el.addClass('x-grid3-col-dd');
54388 };
54389
54390 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54391     handleMouseDown : function(e){
54392
54393     },
54394
54395     callHandleMouseDown : function(e){
54396         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54397     }
54398 });
54399 /*
54400  * Based on:
54401  * Ext JS Library 1.1.1
54402  * Copyright(c) 2006-2007, Ext JS, LLC.
54403  *
54404  * Originally Released Under LGPL - original licence link has changed is not relivant.
54405  *
54406  * Fork - LGPL
54407  * <script type="text/javascript">
54408  */
54409  
54410 // private
54411 // This is a support class used internally by the Grid components
54412 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54413     this.grid = grid;
54414     this.view = grid.getView();
54415     this.proxy = this.view.resizeProxy;
54416     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54417         "gridSplitters" + this.grid.getGridEl().id, {
54418         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54419     });
54420     this.setHandleElId(Roo.id(hd));
54421     this.setOuterHandleElId(Roo.id(hd2));
54422     this.scroll = false;
54423 };
54424 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54425     fly: Roo.Element.fly,
54426
54427     b4StartDrag : function(x, y){
54428         this.view.headersDisabled = true;
54429         this.proxy.setHeight(this.view.mainWrap.getHeight());
54430         var w = this.cm.getColumnWidth(this.cellIndex);
54431         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54432         this.resetConstraints();
54433         this.setXConstraint(minw, 1000);
54434         this.setYConstraint(0, 0);
54435         this.minX = x - minw;
54436         this.maxX = x + 1000;
54437         this.startPos = x;
54438         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54439     },
54440
54441
54442     handleMouseDown : function(e){
54443         ev = Roo.EventObject.setEvent(e);
54444         var t = this.fly(ev.getTarget());
54445         if(t.hasClass("x-grid-split")){
54446             this.cellIndex = this.view.getCellIndex(t.dom);
54447             this.split = t.dom;
54448             this.cm = this.grid.colModel;
54449             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54450                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54451             }
54452         }
54453     },
54454
54455     endDrag : function(e){
54456         this.view.headersDisabled = false;
54457         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54458         var diff = endX - this.startPos;
54459         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54460     },
54461
54462     autoOffset : function(){
54463         this.setDelta(0,0);
54464     }
54465 });/*
54466  * Based on:
54467  * Ext JS Library 1.1.1
54468  * Copyright(c) 2006-2007, Ext JS, LLC.
54469  *
54470  * Originally Released Under LGPL - original licence link has changed is not relivant.
54471  *
54472  * Fork - LGPL
54473  * <script type="text/javascript">
54474  */
54475  
54476 // private
54477 // This is a support class used internally by the Grid components
54478 Roo.grid.GridDragZone = function(grid, config){
54479     this.view = grid.getView();
54480     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54481     if(this.view.lockedBody){
54482         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54483         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54484     }
54485     this.scroll = false;
54486     this.grid = grid;
54487     this.ddel = document.createElement('div');
54488     this.ddel.className = 'x-grid-dd-wrap';
54489 };
54490
54491 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54492     ddGroup : "GridDD",
54493
54494     getDragData : function(e){
54495         var t = Roo.lib.Event.getTarget(e);
54496         var rowIndex = this.view.findRowIndex(t);
54497         var sm = this.grid.selModel;
54498             
54499         //Roo.log(rowIndex);
54500         
54501         if (sm.getSelectedCell) {
54502             // cell selection..
54503             if (!sm.getSelectedCell()) {
54504                 return false;
54505             }
54506             if (rowIndex != sm.getSelectedCell()[0]) {
54507                 return false;
54508             }
54509         
54510         }
54511         
54512         if(rowIndex !== false){
54513             
54514             // if editorgrid.. 
54515             
54516             
54517             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54518                
54519             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54520               //  
54521             //}
54522             if (e.hasModifier()){
54523                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54524             }
54525             
54526             Roo.log("getDragData");
54527             
54528             return {
54529                 grid: this.grid,
54530                 ddel: this.ddel,
54531                 rowIndex: rowIndex,
54532                 selections:sm.getSelections ? sm.getSelections() : (
54533                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54534                 )
54535             };
54536         }
54537         return false;
54538     },
54539
54540     onInitDrag : function(e){
54541         var data = this.dragData;
54542         this.ddel.innerHTML = this.grid.getDragDropText();
54543         this.proxy.update(this.ddel);
54544         // fire start drag?
54545     },
54546
54547     afterRepair : function(){
54548         this.dragging = false;
54549     },
54550
54551     getRepairXY : function(e, data){
54552         return false;
54553     },
54554
54555     onEndDrag : function(data, e){
54556         // fire end drag?
54557     },
54558
54559     onValidDrop : function(dd, e, id){
54560         // fire drag drop?
54561         this.hideProxy();
54562     },
54563
54564     beforeInvalidDrop : function(e, id){
54565
54566     }
54567 });/*
54568  * Based on:
54569  * Ext JS Library 1.1.1
54570  * Copyright(c) 2006-2007, Ext JS, LLC.
54571  *
54572  * Originally Released Under LGPL - original licence link has changed is not relivant.
54573  *
54574  * Fork - LGPL
54575  * <script type="text/javascript">
54576  */
54577  
54578
54579 /**
54580  * @class Roo.grid.ColumnModel
54581  * @extends Roo.util.Observable
54582  * This is the default implementation of a ColumnModel used by the Grid. It defines
54583  * the columns in the grid.
54584  * <br>Usage:<br>
54585  <pre><code>
54586  var colModel = new Roo.grid.ColumnModel([
54587         {header: "Ticker", width: 60, sortable: true, locked: true},
54588         {header: "Company Name", width: 150, sortable: true},
54589         {header: "Market Cap.", width: 100, sortable: true},
54590         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54591         {header: "Employees", width: 100, sortable: true, resizable: false}
54592  ]);
54593  </code></pre>
54594  * <p>
54595  
54596  * The config options listed for this class are options which may appear in each
54597  * individual column definition.
54598  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54599  * @constructor
54600  * @param {Object} config An Array of column config objects. See this class's
54601  * config objects for details.
54602 */
54603 Roo.grid.ColumnModel = function(config){
54604         /**
54605      * The config passed into the constructor
54606      */
54607     this.config = config;
54608     this.lookup = {};
54609
54610     // if no id, create one
54611     // if the column does not have a dataIndex mapping,
54612     // map it to the order it is in the config
54613     for(var i = 0, len = config.length; i < len; i++){
54614         var c = config[i];
54615         if(typeof c.dataIndex == "undefined"){
54616             c.dataIndex = i;
54617         }
54618         if(typeof c.renderer == "string"){
54619             c.renderer = Roo.util.Format[c.renderer];
54620         }
54621         if(typeof c.id == "undefined"){
54622             c.id = Roo.id();
54623         }
54624         if(c.editor && c.editor.xtype){
54625             c.editor  = Roo.factory(c.editor, Roo.grid);
54626         }
54627         if(c.editor && c.editor.isFormField){
54628             c.editor = new Roo.grid.GridEditor(c.editor);
54629         }
54630         this.lookup[c.id] = c;
54631     }
54632
54633     /**
54634      * The width of columns which have no width specified (defaults to 100)
54635      * @type Number
54636      */
54637     this.defaultWidth = 100;
54638
54639     /**
54640      * Default sortable of columns which have no sortable specified (defaults to false)
54641      * @type Boolean
54642      */
54643     this.defaultSortable = false;
54644
54645     this.addEvents({
54646         /**
54647              * @event widthchange
54648              * Fires when the width of a column changes.
54649              * @param {ColumnModel} this
54650              * @param {Number} columnIndex The column index
54651              * @param {Number} newWidth The new width
54652              */
54653             "widthchange": true,
54654         /**
54655              * @event headerchange
54656              * Fires when the text of a header changes.
54657              * @param {ColumnModel} this
54658              * @param {Number} columnIndex The column index
54659              * @param {Number} newText The new header text
54660              */
54661             "headerchange": true,
54662         /**
54663              * @event hiddenchange
54664              * Fires when a column is hidden or "unhidden".
54665              * @param {ColumnModel} this
54666              * @param {Number} columnIndex The column index
54667              * @param {Boolean} hidden true if hidden, false otherwise
54668              */
54669             "hiddenchange": true,
54670             /**
54671          * @event columnmoved
54672          * Fires when a column is moved.
54673          * @param {ColumnModel} this
54674          * @param {Number} oldIndex
54675          * @param {Number} newIndex
54676          */
54677         "columnmoved" : true,
54678         /**
54679          * @event columlockchange
54680          * Fires when a column's locked state is changed
54681          * @param {ColumnModel} this
54682          * @param {Number} colIndex
54683          * @param {Boolean} locked true if locked
54684          */
54685         "columnlockchange" : true
54686     });
54687     Roo.grid.ColumnModel.superclass.constructor.call(this);
54688 };
54689 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54690     /**
54691      * @cfg {String} header The header text to display in the Grid view.
54692      */
54693     /**
54694      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54695      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54696      * specified, the column's index is used as an index into the Record's data Array.
54697      */
54698     /**
54699      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54700      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54701      */
54702     /**
54703      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54704      * Defaults to the value of the {@link #defaultSortable} property.
54705      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54706      */
54707     /**
54708      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54709      */
54710     /**
54711      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54712      */
54713     /**
54714      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54715      */
54716     /**
54717      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54718      */
54719     /**
54720      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54721      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54722      * default renderer uses the raw data value.
54723      */
54724        /**
54725      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54726      */
54727     /**
54728      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54729      */
54730
54731     /**
54732      * Returns the id of the column at the specified index.
54733      * @param {Number} index The column index
54734      * @return {String} the id
54735      */
54736     getColumnId : function(index){
54737         return this.config[index].id;
54738     },
54739
54740     /**
54741      * Returns the column for a specified id.
54742      * @param {String} id The column id
54743      * @return {Object} the column
54744      */
54745     getColumnById : function(id){
54746         return this.lookup[id];
54747     },
54748
54749     
54750     /**
54751      * Returns the column for a specified dataIndex.
54752      * @param {String} dataIndex The column dataIndex
54753      * @return {Object|Boolean} the column or false if not found
54754      */
54755     getColumnByDataIndex: function(dataIndex){
54756         var index = this.findColumnIndex(dataIndex);
54757         return index > -1 ? this.config[index] : false;
54758     },
54759     
54760     /**
54761      * Returns the index for a specified column id.
54762      * @param {String} id The column id
54763      * @return {Number} the index, or -1 if not found
54764      */
54765     getIndexById : function(id){
54766         for(var i = 0, len = this.config.length; i < len; i++){
54767             if(this.config[i].id == id){
54768                 return i;
54769             }
54770         }
54771         return -1;
54772     },
54773     
54774     /**
54775      * Returns the index for a specified column dataIndex.
54776      * @param {String} dataIndex The column dataIndex
54777      * @return {Number} the index, or -1 if not found
54778      */
54779     
54780     findColumnIndex : function(dataIndex){
54781         for(var i = 0, len = this.config.length; i < len; i++){
54782             if(this.config[i].dataIndex == dataIndex){
54783                 return i;
54784             }
54785         }
54786         return -1;
54787     },
54788     
54789     
54790     moveColumn : function(oldIndex, newIndex){
54791         var c = this.config[oldIndex];
54792         this.config.splice(oldIndex, 1);
54793         this.config.splice(newIndex, 0, c);
54794         this.dataMap = null;
54795         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54796     },
54797
54798     isLocked : function(colIndex){
54799         return this.config[colIndex].locked === true;
54800     },
54801
54802     setLocked : function(colIndex, value, suppressEvent){
54803         if(this.isLocked(colIndex) == value){
54804             return;
54805         }
54806         this.config[colIndex].locked = value;
54807         if(!suppressEvent){
54808             this.fireEvent("columnlockchange", this, colIndex, value);
54809         }
54810     },
54811
54812     getTotalLockedWidth : function(){
54813         var totalWidth = 0;
54814         for(var i = 0; i < this.config.length; i++){
54815             if(this.isLocked(i) && !this.isHidden(i)){
54816                 this.totalWidth += this.getColumnWidth(i);
54817             }
54818         }
54819         return totalWidth;
54820     },
54821
54822     getLockedCount : function(){
54823         for(var i = 0, len = this.config.length; i < len; i++){
54824             if(!this.isLocked(i)){
54825                 return i;
54826             }
54827         }
54828     },
54829
54830     /**
54831      * Returns the number of columns.
54832      * @return {Number}
54833      */
54834     getColumnCount : function(visibleOnly){
54835         if(visibleOnly === true){
54836             var c = 0;
54837             for(var i = 0, len = this.config.length; i < len; i++){
54838                 if(!this.isHidden(i)){
54839                     c++;
54840                 }
54841             }
54842             return c;
54843         }
54844         return this.config.length;
54845     },
54846
54847     /**
54848      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54849      * @param {Function} fn
54850      * @param {Object} scope (optional)
54851      * @return {Array} result
54852      */
54853     getColumnsBy : function(fn, scope){
54854         var r = [];
54855         for(var i = 0, len = this.config.length; i < len; i++){
54856             var c = this.config[i];
54857             if(fn.call(scope||this, c, i) === true){
54858                 r[r.length] = c;
54859             }
54860         }
54861         return r;
54862     },
54863
54864     /**
54865      * Returns true if the specified column is sortable.
54866      * @param {Number} col The column index
54867      * @return {Boolean}
54868      */
54869     isSortable : function(col){
54870         if(typeof this.config[col].sortable == "undefined"){
54871             return this.defaultSortable;
54872         }
54873         return this.config[col].sortable;
54874     },
54875
54876     /**
54877      * Returns the rendering (formatting) function defined for the column.
54878      * @param {Number} col The column index.
54879      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54880      */
54881     getRenderer : function(col){
54882         if(!this.config[col].renderer){
54883             return Roo.grid.ColumnModel.defaultRenderer;
54884         }
54885         return this.config[col].renderer;
54886     },
54887
54888     /**
54889      * Sets the rendering (formatting) function for a column.
54890      * @param {Number} col The column index
54891      * @param {Function} fn The function to use to process the cell's raw data
54892      * to return HTML markup for the grid view. The render function is called with
54893      * the following parameters:<ul>
54894      * <li>Data value.</li>
54895      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54896      * <li>css A CSS style string to apply to the table cell.</li>
54897      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54898      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54899      * <li>Row index</li>
54900      * <li>Column index</li>
54901      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54902      */
54903     setRenderer : function(col, fn){
54904         this.config[col].renderer = fn;
54905     },
54906
54907     /**
54908      * Returns the width for the specified column.
54909      * @param {Number} col The column index
54910      * @return {Number}
54911      */
54912     getColumnWidth : function(col){
54913         return this.config[col].width * 1 || this.defaultWidth;
54914     },
54915
54916     /**
54917      * Sets the width for a column.
54918      * @param {Number} col The column index
54919      * @param {Number} width The new width
54920      */
54921     setColumnWidth : function(col, width, suppressEvent){
54922         this.config[col].width = width;
54923         this.totalWidth = null;
54924         if(!suppressEvent){
54925              this.fireEvent("widthchange", this, col, width);
54926         }
54927     },
54928
54929     /**
54930      * Returns the total width of all columns.
54931      * @param {Boolean} includeHidden True to include hidden column widths
54932      * @return {Number}
54933      */
54934     getTotalWidth : function(includeHidden){
54935         if(!this.totalWidth){
54936             this.totalWidth = 0;
54937             for(var i = 0, len = this.config.length; i < len; i++){
54938                 if(includeHidden || !this.isHidden(i)){
54939                     this.totalWidth += this.getColumnWidth(i);
54940                 }
54941             }
54942         }
54943         return this.totalWidth;
54944     },
54945
54946     /**
54947      * Returns the header for the specified column.
54948      * @param {Number} col The column index
54949      * @return {String}
54950      */
54951     getColumnHeader : function(col){
54952         return this.config[col].header;
54953     },
54954
54955     /**
54956      * Sets the header for a column.
54957      * @param {Number} col The column index
54958      * @param {String} header The new header
54959      */
54960     setColumnHeader : function(col, header){
54961         this.config[col].header = header;
54962         this.fireEvent("headerchange", this, col, header);
54963     },
54964
54965     /**
54966      * Returns the tooltip for the specified column.
54967      * @param {Number} col The column index
54968      * @return {String}
54969      */
54970     getColumnTooltip : function(col){
54971             return this.config[col].tooltip;
54972     },
54973     /**
54974      * Sets the tooltip for a column.
54975      * @param {Number} col The column index
54976      * @param {String} tooltip The new tooltip
54977      */
54978     setColumnTooltip : function(col, tooltip){
54979             this.config[col].tooltip = tooltip;
54980     },
54981
54982     /**
54983      * Returns the dataIndex for the specified column.
54984      * @param {Number} col The column index
54985      * @return {Number}
54986      */
54987     getDataIndex : function(col){
54988         return this.config[col].dataIndex;
54989     },
54990
54991     /**
54992      * Sets the dataIndex for a column.
54993      * @param {Number} col The column index
54994      * @param {Number} dataIndex The new dataIndex
54995      */
54996     setDataIndex : function(col, dataIndex){
54997         this.config[col].dataIndex = dataIndex;
54998     },
54999
55000     
55001     
55002     /**
55003      * Returns true if the cell is editable.
55004      * @param {Number} colIndex The column index
55005      * @param {Number} rowIndex The row index
55006      * @return {Boolean}
55007      */
55008     isCellEditable : function(colIndex, rowIndex){
55009         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55010     },
55011
55012     /**
55013      * Returns the editor defined for the cell/column.
55014      * return false or null to disable editing.
55015      * @param {Number} colIndex The column index
55016      * @param {Number} rowIndex The row index
55017      * @return {Object}
55018      */
55019     getCellEditor : function(colIndex, rowIndex){
55020         return this.config[colIndex].editor;
55021     },
55022
55023     /**
55024      * Sets if a column is editable.
55025      * @param {Number} col The column index
55026      * @param {Boolean} editable True if the column is editable
55027      */
55028     setEditable : function(col, editable){
55029         this.config[col].editable = editable;
55030     },
55031
55032
55033     /**
55034      * Returns true if the column is hidden.
55035      * @param {Number} colIndex The column index
55036      * @return {Boolean}
55037      */
55038     isHidden : function(colIndex){
55039         return this.config[colIndex].hidden;
55040     },
55041
55042
55043     /**
55044      * Returns true if the column width cannot be changed
55045      */
55046     isFixed : function(colIndex){
55047         return this.config[colIndex].fixed;
55048     },
55049
55050     /**
55051      * Returns true if the column can be resized
55052      * @return {Boolean}
55053      */
55054     isResizable : function(colIndex){
55055         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55056     },
55057     /**
55058      * Sets if a column is hidden.
55059      * @param {Number} colIndex The column index
55060      * @param {Boolean} hidden True if the column is hidden
55061      */
55062     setHidden : function(colIndex, hidden){
55063         this.config[colIndex].hidden = hidden;
55064         this.totalWidth = null;
55065         this.fireEvent("hiddenchange", this, colIndex, hidden);
55066     },
55067
55068     /**
55069      * Sets the editor for a column.
55070      * @param {Number} col The column index
55071      * @param {Object} editor The editor object
55072      */
55073     setEditor : function(col, editor){
55074         this.config[col].editor = editor;
55075     }
55076 });
55077
55078 Roo.grid.ColumnModel.defaultRenderer = function(value){
55079         if(typeof value == "string" && value.length < 1){
55080             return "&#160;";
55081         }
55082         return value;
55083 };
55084
55085 // Alias for backwards compatibility
55086 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55087 /*
55088  * Based on:
55089  * Ext JS Library 1.1.1
55090  * Copyright(c) 2006-2007, Ext JS, LLC.
55091  *
55092  * Originally Released Under LGPL - original licence link has changed is not relivant.
55093  *
55094  * Fork - LGPL
55095  * <script type="text/javascript">
55096  */
55097
55098 /**
55099  * @class Roo.grid.AbstractSelectionModel
55100  * @extends Roo.util.Observable
55101  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55102  * implemented by descendant classes.  This class should not be directly instantiated.
55103  * @constructor
55104  */
55105 Roo.grid.AbstractSelectionModel = function(){
55106     this.locked = false;
55107     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55108 };
55109
55110 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55111     /** @ignore Called by the grid automatically. Do not call directly. */
55112     init : function(grid){
55113         this.grid = grid;
55114         this.initEvents();
55115     },
55116
55117     /**
55118      * Locks the selections.
55119      */
55120     lock : function(){
55121         this.locked = true;
55122     },
55123
55124     /**
55125      * Unlocks the selections.
55126      */
55127     unlock : function(){
55128         this.locked = false;
55129     },
55130
55131     /**
55132      * Returns true if the selections are locked.
55133      * @return {Boolean}
55134      */
55135     isLocked : function(){
55136         return this.locked;
55137     }
55138 });/*
55139  * Based on:
55140  * Ext JS Library 1.1.1
55141  * Copyright(c) 2006-2007, Ext JS, LLC.
55142  *
55143  * Originally Released Under LGPL - original licence link has changed is not relivant.
55144  *
55145  * Fork - LGPL
55146  * <script type="text/javascript">
55147  */
55148 /**
55149  * @extends Roo.grid.AbstractSelectionModel
55150  * @class Roo.grid.RowSelectionModel
55151  * The default SelectionModel used by {@link Roo.grid.Grid}.
55152  * It supports multiple selections and keyboard selection/navigation. 
55153  * @constructor
55154  * @param {Object} config
55155  */
55156 Roo.grid.RowSelectionModel = function(config){
55157     Roo.apply(this, config);
55158     this.selections = new Roo.util.MixedCollection(false, function(o){
55159         return o.id;
55160     });
55161
55162     this.last = false;
55163     this.lastActive = false;
55164
55165     this.addEvents({
55166         /**
55167              * @event selectionchange
55168              * Fires when the selection changes
55169              * @param {SelectionModel} this
55170              */
55171             "selectionchange" : true,
55172         /**
55173              * @event afterselectionchange
55174              * Fires after the selection changes (eg. by key press or clicking)
55175              * @param {SelectionModel} this
55176              */
55177             "afterselectionchange" : true,
55178         /**
55179              * @event beforerowselect
55180              * Fires when a row is selected being selected, return false to cancel.
55181              * @param {SelectionModel} this
55182              * @param {Number} rowIndex The selected index
55183              * @param {Boolean} keepExisting False if other selections will be cleared
55184              */
55185             "beforerowselect" : true,
55186         /**
55187              * @event rowselect
55188              * Fires when a row is selected.
55189              * @param {SelectionModel} this
55190              * @param {Number} rowIndex The selected index
55191              * @param {Roo.data.Record} r The record
55192              */
55193             "rowselect" : true,
55194         /**
55195              * @event rowdeselect
55196              * Fires when a row is deselected.
55197              * @param {SelectionModel} this
55198              * @param {Number} rowIndex The selected index
55199              */
55200         "rowdeselect" : true
55201     });
55202     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55203     this.locked = false;
55204 };
55205
55206 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55207     /**
55208      * @cfg {Boolean} singleSelect
55209      * True to allow selection of only one row at a time (defaults to false)
55210      */
55211     singleSelect : false,
55212
55213     // private
55214     initEvents : function(){
55215
55216         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55217             this.grid.on("mousedown", this.handleMouseDown, this);
55218         }else{ // allow click to work like normal
55219             this.grid.on("rowclick", this.handleDragableRowClick, this);
55220         }
55221
55222         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55223             "up" : function(e){
55224                 if(!e.shiftKey){
55225                     this.selectPrevious(e.shiftKey);
55226                 }else if(this.last !== false && this.lastActive !== false){
55227                     var last = this.last;
55228                     this.selectRange(this.last,  this.lastActive-1);
55229                     this.grid.getView().focusRow(this.lastActive);
55230                     if(last !== false){
55231                         this.last = last;
55232                     }
55233                 }else{
55234                     this.selectFirstRow();
55235                 }
55236                 this.fireEvent("afterselectionchange", this);
55237             },
55238             "down" : function(e){
55239                 if(!e.shiftKey){
55240                     this.selectNext(e.shiftKey);
55241                 }else if(this.last !== false && this.lastActive !== false){
55242                     var last = this.last;
55243                     this.selectRange(this.last,  this.lastActive+1);
55244                     this.grid.getView().focusRow(this.lastActive);
55245                     if(last !== false){
55246                         this.last = last;
55247                     }
55248                 }else{
55249                     this.selectFirstRow();
55250                 }
55251                 this.fireEvent("afterselectionchange", this);
55252             },
55253             scope: this
55254         });
55255
55256         var view = this.grid.view;
55257         view.on("refresh", this.onRefresh, this);
55258         view.on("rowupdated", this.onRowUpdated, this);
55259         view.on("rowremoved", this.onRemove, this);
55260     },
55261
55262     // private
55263     onRefresh : function(){
55264         var ds = this.grid.dataSource, i, v = this.grid.view;
55265         var s = this.selections;
55266         s.each(function(r){
55267             if((i = ds.indexOfId(r.id)) != -1){
55268                 v.onRowSelect(i);
55269             }else{
55270                 s.remove(r);
55271             }
55272         });
55273     },
55274
55275     // private
55276     onRemove : function(v, index, r){
55277         this.selections.remove(r);
55278     },
55279
55280     // private
55281     onRowUpdated : function(v, index, r){
55282         if(this.isSelected(r)){
55283             v.onRowSelect(index);
55284         }
55285     },
55286
55287     /**
55288      * Select records.
55289      * @param {Array} records The records to select
55290      * @param {Boolean} keepExisting (optional) True to keep existing selections
55291      */
55292     selectRecords : function(records, keepExisting){
55293         if(!keepExisting){
55294             this.clearSelections();
55295         }
55296         var ds = this.grid.dataSource;
55297         for(var i = 0, len = records.length; i < len; i++){
55298             this.selectRow(ds.indexOf(records[i]), true);
55299         }
55300     },
55301
55302     /**
55303      * Gets the number of selected rows.
55304      * @return {Number}
55305      */
55306     getCount : function(){
55307         return this.selections.length;
55308     },
55309
55310     /**
55311      * Selects the first row in the grid.
55312      */
55313     selectFirstRow : function(){
55314         this.selectRow(0);
55315     },
55316
55317     /**
55318      * Select the last row.
55319      * @param {Boolean} keepExisting (optional) True to keep existing selections
55320      */
55321     selectLastRow : function(keepExisting){
55322         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55323     },
55324
55325     /**
55326      * Selects the row immediately following the last selected row.
55327      * @param {Boolean} keepExisting (optional) True to keep existing selections
55328      */
55329     selectNext : function(keepExisting){
55330         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55331             this.selectRow(this.last+1, keepExisting);
55332             this.grid.getView().focusRow(this.last);
55333         }
55334     },
55335
55336     /**
55337      * Selects the row that precedes the last selected row.
55338      * @param {Boolean} keepExisting (optional) True to keep existing selections
55339      */
55340     selectPrevious : function(keepExisting){
55341         if(this.last){
55342             this.selectRow(this.last-1, keepExisting);
55343             this.grid.getView().focusRow(this.last);
55344         }
55345     },
55346
55347     /**
55348      * Returns the selected records
55349      * @return {Array} Array of selected records
55350      */
55351     getSelections : function(){
55352         return [].concat(this.selections.items);
55353     },
55354
55355     /**
55356      * Returns the first selected record.
55357      * @return {Record}
55358      */
55359     getSelected : function(){
55360         return this.selections.itemAt(0);
55361     },
55362
55363
55364     /**
55365      * Clears all selections.
55366      */
55367     clearSelections : function(fast){
55368         if(this.locked) return;
55369         if(fast !== true){
55370             var ds = this.grid.dataSource;
55371             var s = this.selections;
55372             s.each(function(r){
55373                 this.deselectRow(ds.indexOfId(r.id));
55374             }, this);
55375             s.clear();
55376         }else{
55377             this.selections.clear();
55378         }
55379         this.last = false;
55380     },
55381
55382
55383     /**
55384      * Selects all rows.
55385      */
55386     selectAll : function(){
55387         if(this.locked) return;
55388         this.selections.clear();
55389         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55390             this.selectRow(i, true);
55391         }
55392     },
55393
55394     /**
55395      * Returns True if there is a selection.
55396      * @return {Boolean}
55397      */
55398     hasSelection : function(){
55399         return this.selections.length > 0;
55400     },
55401
55402     /**
55403      * Returns True if the specified row is selected.
55404      * @param {Number/Record} record The record or index of the record to check
55405      * @return {Boolean}
55406      */
55407     isSelected : function(index){
55408         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55409         return (r && this.selections.key(r.id) ? true : false);
55410     },
55411
55412     /**
55413      * Returns True if the specified record id is selected.
55414      * @param {String} id The id of record to check
55415      * @return {Boolean}
55416      */
55417     isIdSelected : function(id){
55418         return (this.selections.key(id) ? true : false);
55419     },
55420
55421     // private
55422     handleMouseDown : function(e, t){
55423         var view = this.grid.getView(), rowIndex;
55424         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55425             return;
55426         };
55427         if(e.shiftKey && this.last !== false){
55428             var last = this.last;
55429             this.selectRange(last, rowIndex, e.ctrlKey);
55430             this.last = last; // reset the last
55431             view.focusRow(rowIndex);
55432         }else{
55433             var isSelected = this.isSelected(rowIndex);
55434             if(e.button !== 0 && isSelected){
55435                 view.focusRow(rowIndex);
55436             }else if(e.ctrlKey && isSelected){
55437                 this.deselectRow(rowIndex);
55438             }else if(!isSelected){
55439                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55440                 view.focusRow(rowIndex);
55441             }
55442         }
55443         this.fireEvent("afterselectionchange", this);
55444     },
55445     // private
55446     handleDragableRowClick :  function(grid, rowIndex, e) 
55447     {
55448         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55449             this.selectRow(rowIndex, false);
55450             grid.view.focusRow(rowIndex);
55451              this.fireEvent("afterselectionchange", this);
55452         }
55453     },
55454     
55455     /**
55456      * Selects multiple rows.
55457      * @param {Array} rows Array of the indexes of the row to select
55458      * @param {Boolean} keepExisting (optional) True to keep existing selections
55459      */
55460     selectRows : function(rows, keepExisting){
55461         if(!keepExisting){
55462             this.clearSelections();
55463         }
55464         for(var i = 0, len = rows.length; i < len; i++){
55465             this.selectRow(rows[i], true);
55466         }
55467     },
55468
55469     /**
55470      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55471      * @param {Number} startRow The index of the first row in the range
55472      * @param {Number} endRow The index of the last row in the range
55473      * @param {Boolean} keepExisting (optional) True to retain existing selections
55474      */
55475     selectRange : function(startRow, endRow, keepExisting){
55476         if(this.locked) return;
55477         if(!keepExisting){
55478             this.clearSelections();
55479         }
55480         if(startRow <= endRow){
55481             for(var i = startRow; i <= endRow; i++){
55482                 this.selectRow(i, true);
55483             }
55484         }else{
55485             for(var i = startRow; i >= endRow; i--){
55486                 this.selectRow(i, true);
55487             }
55488         }
55489     },
55490
55491     /**
55492      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55493      * @param {Number} startRow The index of the first row in the range
55494      * @param {Number} endRow The index of the last row in the range
55495      */
55496     deselectRange : function(startRow, endRow, preventViewNotify){
55497         if(this.locked) return;
55498         for(var i = startRow; i <= endRow; i++){
55499             this.deselectRow(i, preventViewNotify);
55500         }
55501     },
55502
55503     /**
55504      * Selects a row.
55505      * @param {Number} row The index of the row to select
55506      * @param {Boolean} keepExisting (optional) True to keep existing selections
55507      */
55508     selectRow : function(index, keepExisting, preventViewNotify){
55509         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55510         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55511             if(!keepExisting || this.singleSelect){
55512                 this.clearSelections();
55513             }
55514             var r = this.grid.dataSource.getAt(index);
55515             this.selections.add(r);
55516             this.last = this.lastActive = index;
55517             if(!preventViewNotify){
55518                 this.grid.getView().onRowSelect(index);
55519             }
55520             this.fireEvent("rowselect", this, index, r);
55521             this.fireEvent("selectionchange", this);
55522         }
55523     },
55524
55525     /**
55526      * Deselects a row.
55527      * @param {Number} row The index of the row to deselect
55528      */
55529     deselectRow : function(index, preventViewNotify){
55530         if(this.locked) return;
55531         if(this.last == index){
55532             this.last = false;
55533         }
55534         if(this.lastActive == index){
55535             this.lastActive = false;
55536         }
55537         var r = this.grid.dataSource.getAt(index);
55538         this.selections.remove(r);
55539         if(!preventViewNotify){
55540             this.grid.getView().onRowDeselect(index);
55541         }
55542         this.fireEvent("rowdeselect", this, index);
55543         this.fireEvent("selectionchange", this);
55544     },
55545
55546     // private
55547     restoreLast : function(){
55548         if(this._last){
55549             this.last = this._last;
55550         }
55551     },
55552
55553     // private
55554     acceptsNav : function(row, col, cm){
55555         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55556     },
55557
55558     // private
55559     onEditorKey : function(field, e){
55560         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55561         if(k == e.TAB){
55562             e.stopEvent();
55563             ed.completeEdit();
55564             if(e.shiftKey){
55565                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55566             }else{
55567                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55568             }
55569         }else if(k == e.ENTER && !e.ctrlKey){
55570             e.stopEvent();
55571             ed.completeEdit();
55572             if(e.shiftKey){
55573                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55574             }else{
55575                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55576             }
55577         }else if(k == e.ESC){
55578             ed.cancelEdit();
55579         }
55580         if(newCell){
55581             g.startEditing(newCell[0], newCell[1]);
55582         }
55583     }
55584 });/*
55585  * Based on:
55586  * Ext JS Library 1.1.1
55587  * Copyright(c) 2006-2007, Ext JS, LLC.
55588  *
55589  * Originally Released Under LGPL - original licence link has changed is not relivant.
55590  *
55591  * Fork - LGPL
55592  * <script type="text/javascript">
55593  */
55594 /**
55595  * @class Roo.grid.CellSelectionModel
55596  * @extends Roo.grid.AbstractSelectionModel
55597  * This class provides the basic implementation for cell selection in a grid.
55598  * @constructor
55599  * @param {Object} config The object containing the configuration of this model.
55600  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55601  */
55602 Roo.grid.CellSelectionModel = function(config){
55603     Roo.apply(this, config);
55604
55605     this.selection = null;
55606
55607     this.addEvents({
55608         /**
55609              * @event beforerowselect
55610              * Fires before a cell is selected.
55611              * @param {SelectionModel} this
55612              * @param {Number} rowIndex The selected row index
55613              * @param {Number} colIndex The selected cell index
55614              */
55615             "beforecellselect" : true,
55616         /**
55617              * @event cellselect
55618              * Fires when a cell is selected.
55619              * @param {SelectionModel} this
55620              * @param {Number} rowIndex The selected row index
55621              * @param {Number} colIndex The selected cell index
55622              */
55623             "cellselect" : true,
55624         /**
55625              * @event selectionchange
55626              * Fires when the active selection changes.
55627              * @param {SelectionModel} this
55628              * @param {Object} selection null for no selection or an object (o) with two properties
55629                 <ul>
55630                 <li>o.record: the record object for the row the selection is in</li>
55631                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55632                 </ul>
55633              */
55634             "selectionchange" : true,
55635         /**
55636              * @event tabend
55637              * Fires when the tab (or enter) was pressed on the last editable cell
55638              * You can use this to trigger add new row.
55639              * @param {SelectionModel} this
55640              */
55641             "tabend" : true,
55642          /**
55643              * @event beforeeditnext
55644              * Fires before the next editable sell is made active
55645              * You can use this to skip to another cell or fire the tabend
55646              *    if you set cell to false
55647              * @param {Object} eventdata object : { cell : [ row, col ] } 
55648              */
55649             "beforeeditnext" : true
55650     });
55651     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55652 };
55653
55654 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55655     
55656     enter_is_tab: false,
55657
55658     /** @ignore */
55659     initEvents : function(){
55660         this.grid.on("mousedown", this.handleMouseDown, this);
55661         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55662         var view = this.grid.view;
55663         view.on("refresh", this.onViewChange, this);
55664         view.on("rowupdated", this.onRowUpdated, this);
55665         view.on("beforerowremoved", this.clearSelections, this);
55666         view.on("beforerowsinserted", this.clearSelections, this);
55667         if(this.grid.isEditor){
55668             this.grid.on("beforeedit", this.beforeEdit,  this);
55669         }
55670     },
55671
55672         //private
55673     beforeEdit : function(e){
55674         this.select(e.row, e.column, false, true, e.record);
55675     },
55676
55677         //private
55678     onRowUpdated : function(v, index, r){
55679         if(this.selection && this.selection.record == r){
55680             v.onCellSelect(index, this.selection.cell[1]);
55681         }
55682     },
55683
55684         //private
55685     onViewChange : function(){
55686         this.clearSelections(true);
55687     },
55688
55689         /**
55690          * Returns the currently selected cell,.
55691          * @return {Array} The selected cell (row, column) or null if none selected.
55692          */
55693     getSelectedCell : function(){
55694         return this.selection ? this.selection.cell : null;
55695     },
55696
55697     /**
55698      * Clears all selections.
55699      * @param {Boolean} true to prevent the gridview from being notified about the change.
55700      */
55701     clearSelections : function(preventNotify){
55702         var s = this.selection;
55703         if(s){
55704             if(preventNotify !== true){
55705                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55706             }
55707             this.selection = null;
55708             this.fireEvent("selectionchange", this, null);
55709         }
55710     },
55711
55712     /**
55713      * Returns true if there is a selection.
55714      * @return {Boolean}
55715      */
55716     hasSelection : function(){
55717         return this.selection ? true : false;
55718     },
55719
55720     /** @ignore */
55721     handleMouseDown : function(e, t){
55722         var v = this.grid.getView();
55723         if(this.isLocked()){
55724             return;
55725         };
55726         var row = v.findRowIndex(t);
55727         var cell = v.findCellIndex(t);
55728         if(row !== false && cell !== false){
55729             this.select(row, cell);
55730         }
55731     },
55732
55733     /**
55734      * Selects a cell.
55735      * @param {Number} rowIndex
55736      * @param {Number} collIndex
55737      */
55738     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55739         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55740             this.clearSelections();
55741             r = r || this.grid.dataSource.getAt(rowIndex);
55742             this.selection = {
55743                 record : r,
55744                 cell : [rowIndex, colIndex]
55745             };
55746             if(!preventViewNotify){
55747                 var v = this.grid.getView();
55748                 v.onCellSelect(rowIndex, colIndex);
55749                 if(preventFocus !== true){
55750                     v.focusCell(rowIndex, colIndex);
55751                 }
55752             }
55753             this.fireEvent("cellselect", this, rowIndex, colIndex);
55754             this.fireEvent("selectionchange", this, this.selection);
55755         }
55756     },
55757
55758         //private
55759     isSelectable : function(rowIndex, colIndex, cm){
55760         return !cm.isHidden(colIndex);
55761     },
55762
55763     /** @ignore */
55764     handleKeyDown : function(e){
55765         //Roo.log('Cell Sel Model handleKeyDown');
55766         if(!e.isNavKeyPress()){
55767             return;
55768         }
55769         var g = this.grid, s = this.selection;
55770         if(!s){
55771             e.stopEvent();
55772             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55773             if(cell){
55774                 this.select(cell[0], cell[1]);
55775             }
55776             return;
55777         }
55778         var sm = this;
55779         var walk = function(row, col, step){
55780             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55781         };
55782         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55783         var newCell;
55784
55785       
55786
55787         switch(k){
55788             case e.TAB:
55789                 // handled by onEditorKey
55790                 if (g.isEditor && g.editing) {
55791                     return;
55792                 }
55793                 if(e.shiftKey) {
55794                     newCell = walk(r, c-1, -1);
55795                 } else {
55796                     newCell = walk(r, c+1, 1);
55797                 }
55798                 break;
55799             
55800             case e.DOWN:
55801                newCell = walk(r+1, c, 1);
55802                 break;
55803             
55804             case e.UP:
55805                 newCell = walk(r-1, c, -1);
55806                 break;
55807             
55808             case e.RIGHT:
55809                 newCell = walk(r, c+1, 1);
55810                 break;
55811             
55812             case e.LEFT:
55813                 newCell = walk(r, c-1, -1);
55814                 break;
55815             
55816             case e.ENTER:
55817                 
55818                 if(g.isEditor && !g.editing){
55819                    g.startEditing(r, c);
55820                    e.stopEvent();
55821                    return;
55822                 }
55823                 
55824                 
55825              break;
55826         };
55827         if(newCell){
55828             this.select(newCell[0], newCell[1]);
55829             e.stopEvent();
55830             
55831         }
55832     },
55833
55834     acceptsNav : function(row, col, cm){
55835         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55836     },
55837     /**
55838      * Selects a cell.
55839      * @param {Number} field (not used) - as it's normally used as a listener
55840      * @param {Number} e - event - fake it by using
55841      *
55842      * var e = Roo.EventObjectImpl.prototype;
55843      * e.keyCode = e.TAB
55844      *
55845      * 
55846      */
55847     onEditorKey : function(field, e){
55848         
55849         var k = e.getKey(),
55850             newCell,
55851             g = this.grid,
55852             ed = g.activeEditor,
55853             forward = false;
55854         ///Roo.log('onEditorKey' + k);
55855         
55856         
55857         if (this.enter_is_tab && k == e.ENTER) {
55858             k = e.TAB;
55859         }
55860         
55861         if(k == e.TAB){
55862             if(e.shiftKey){
55863                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55864             }else{
55865                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55866                 forward = true;
55867             }
55868             
55869             e.stopEvent();
55870             
55871         } else if(k == e.ENTER &&  !e.ctrlKey){
55872             ed.completeEdit();
55873             e.stopEvent();
55874             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55875         
55876                 } else if(k == e.ESC){
55877             ed.cancelEdit();
55878         }
55879                 
55880         if (newCell) {
55881             var ecall = { cell : newCell, forward : forward };
55882             this.fireEvent('beforeeditnext', ecall );
55883             newCell = ecall.cell;
55884                         forward = ecall.forward;
55885         }
55886                 
55887         if(newCell){
55888             //Roo.log('next cell after edit');
55889             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55890         } else if (forward) {
55891             // tabbed past last
55892             this.fireEvent.defer(100, this, ['tabend',this]);
55893         }
55894     }
55895 });/*
55896  * Based on:
55897  * Ext JS Library 1.1.1
55898  * Copyright(c) 2006-2007, Ext JS, LLC.
55899  *
55900  * Originally Released Under LGPL - original licence link has changed is not relivant.
55901  *
55902  * Fork - LGPL
55903  * <script type="text/javascript">
55904  */
55905  
55906 /**
55907  * @class Roo.grid.EditorGrid
55908  * @extends Roo.grid.Grid
55909  * Class for creating and editable grid.
55910  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55911  * The container MUST have some type of size defined for the grid to fill. The container will be 
55912  * automatically set to position relative if it isn't already.
55913  * @param {Object} dataSource The data model to bind to
55914  * @param {Object} colModel The column model with info about this grid's columns
55915  */
55916 Roo.grid.EditorGrid = function(container, config){
55917     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55918     this.getGridEl().addClass("xedit-grid");
55919
55920     if(!this.selModel){
55921         this.selModel = new Roo.grid.CellSelectionModel();
55922     }
55923
55924     this.activeEditor = null;
55925
55926         this.addEvents({
55927             /**
55928              * @event beforeedit
55929              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55930              * <ul style="padding:5px;padding-left:16px;">
55931              * <li>grid - This grid</li>
55932              * <li>record - The record being edited</li>
55933              * <li>field - The field name being edited</li>
55934              * <li>value - The value for the field being edited.</li>
55935              * <li>row - The grid row index</li>
55936              * <li>column - The grid column index</li>
55937              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55938              * </ul>
55939              * @param {Object} e An edit event (see above for description)
55940              */
55941             "beforeedit" : true,
55942             /**
55943              * @event afteredit
55944              * Fires after a cell is edited. <br />
55945              * <ul style="padding:5px;padding-left:16px;">
55946              * <li>grid - This grid</li>
55947              * <li>record - The record being edited</li>
55948              * <li>field - The field name being edited</li>
55949              * <li>value - The value being set</li>
55950              * <li>originalValue - The original value for the field, before the edit.</li>
55951              * <li>row - The grid row index</li>
55952              * <li>column - The grid column index</li>
55953              * </ul>
55954              * @param {Object} e An edit event (see above for description)
55955              */
55956             "afteredit" : true,
55957             /**
55958              * @event validateedit
55959              * Fires after a cell is edited, but before the value is set in the record. 
55960          * You can use this to modify the value being set in the field, Return false
55961              * to cancel the change. The edit event object has the following properties <br />
55962              * <ul style="padding:5px;padding-left:16px;">
55963          * <li>editor - This editor</li>
55964              * <li>grid - This grid</li>
55965              * <li>record - The record being edited</li>
55966              * <li>field - The field name being edited</li>
55967              * <li>value - The value being set</li>
55968              * <li>originalValue - The original value for the field, before the edit.</li>
55969              * <li>row - The grid row index</li>
55970              * <li>column - The grid column index</li>
55971              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55972              * </ul>
55973              * @param {Object} e An edit event (see above for description)
55974              */
55975             "validateedit" : true
55976         });
55977     this.on("bodyscroll", this.stopEditing,  this);
55978     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55979 };
55980
55981 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55982     /**
55983      * @cfg {Number} clicksToEdit
55984      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55985      */
55986     clicksToEdit: 2,
55987
55988     // private
55989     isEditor : true,
55990     // private
55991     trackMouseOver: false, // causes very odd FF errors
55992
55993     onCellDblClick : function(g, row, col){
55994         this.startEditing(row, col);
55995     },
55996
55997     onEditComplete : function(ed, value, startValue){
55998         this.editing = false;
55999         this.activeEditor = null;
56000         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56001         var r = ed.record;
56002         var field = this.colModel.getDataIndex(ed.col);
56003         var e = {
56004             grid: this,
56005             record: r,
56006             field: field,
56007             originalValue: startValue,
56008             value: value,
56009             row: ed.row,
56010             column: ed.col,
56011             cancel:false,
56012             editor: ed
56013         };
56014         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56015         cell.show();
56016           
56017         if(String(value) !== String(startValue)){
56018             
56019             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56020                 r.set(field, e.value);
56021                 // if we are dealing with a combo box..
56022                 // then we also set the 'name' colum to be the displayField
56023                 if (ed.field.displayField && ed.field.name) {
56024                     r.set(ed.field.name, ed.field.el.dom.value);
56025                 }
56026                 
56027                 delete e.cancel; //?? why!!!
56028                 this.fireEvent("afteredit", e);
56029             }
56030         } else {
56031             this.fireEvent("afteredit", e); // always fire it!
56032         }
56033         this.view.focusCell(ed.row, ed.col);
56034     },
56035
56036     /**
56037      * Starts editing the specified for the specified row/column
56038      * @param {Number} rowIndex
56039      * @param {Number} colIndex
56040      */
56041     startEditing : function(row, col){
56042         this.stopEditing();
56043         if(this.colModel.isCellEditable(col, row)){
56044             this.view.ensureVisible(row, col, true);
56045           
56046             var r = this.dataSource.getAt(row);
56047             var field = this.colModel.getDataIndex(col);
56048             var cell = Roo.get(this.view.getCell(row,col));
56049             var e = {
56050                 grid: this,
56051                 record: r,
56052                 field: field,
56053                 value: r.data[field],
56054                 row: row,
56055                 column: col,
56056                 cancel:false 
56057             };
56058             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56059                 this.editing = true;
56060                 var ed = this.colModel.getCellEditor(col, row);
56061                 
56062                 if (!ed) {
56063                     return;
56064                 }
56065                 if(!ed.rendered){
56066                     ed.render(ed.parentEl || document.body);
56067                 }
56068                 ed.field.reset();
56069                
56070                 cell.hide();
56071                 
56072                 (function(){ // complex but required for focus issues in safari, ie and opera
56073                     ed.row = row;
56074                     ed.col = col;
56075                     ed.record = r;
56076                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56077                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56078                     this.activeEditor = ed;
56079                     var v = r.data[field];
56080                     ed.startEdit(this.view.getCell(row, col), v);
56081                     // combo's with 'displayField and name set
56082                     if (ed.field.displayField && ed.field.name) {
56083                         ed.field.el.dom.value = r.data[ed.field.name];
56084                     }
56085                     
56086                     
56087                 }).defer(50, this);
56088             }
56089         }
56090     },
56091         
56092     /**
56093      * Stops any active editing
56094      */
56095     stopEditing : function(){
56096         if(this.activeEditor){
56097             this.activeEditor.completeEdit();
56098         }
56099         this.activeEditor = null;
56100     },
56101         
56102          /**
56103      * Called to get grid's drag proxy text, by default returns this.ddText.
56104      * @return {String}
56105      */
56106     getDragDropText : function(){
56107         var count = this.selModel.getSelectedCell() ? 1 : 0;
56108         return String.format(this.ddText, count, count == 1 ? '' : 's');
56109     }
56110         
56111 });/*
56112  * Based on:
56113  * Ext JS Library 1.1.1
56114  * Copyright(c) 2006-2007, Ext JS, LLC.
56115  *
56116  * Originally Released Under LGPL - original licence link has changed is not relivant.
56117  *
56118  * Fork - LGPL
56119  * <script type="text/javascript">
56120  */
56121
56122 // private - not really -- you end up using it !
56123 // This is a support class used internally by the Grid components
56124
56125 /**
56126  * @class Roo.grid.GridEditor
56127  * @extends Roo.Editor
56128  * Class for creating and editable grid elements.
56129  * @param {Object} config any settings (must include field)
56130  */
56131 Roo.grid.GridEditor = function(field, config){
56132     if (!config && field.field) {
56133         config = field;
56134         field = Roo.factory(config.field, Roo.form);
56135     }
56136     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56137     field.monitorTab = false;
56138 };
56139
56140 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56141     
56142     /**
56143      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56144      */
56145     
56146     alignment: "tl-tl",
56147     autoSize: "width",
56148     hideEl : false,
56149     cls: "x-small-editor x-grid-editor",
56150     shim:false,
56151     shadow:"frame"
56152 });/*
56153  * Based on:
56154  * Ext JS Library 1.1.1
56155  * Copyright(c) 2006-2007, Ext JS, LLC.
56156  *
56157  * Originally Released Under LGPL - original licence link has changed is not relivant.
56158  *
56159  * Fork - LGPL
56160  * <script type="text/javascript">
56161  */
56162   
56163
56164   
56165 Roo.grid.PropertyRecord = Roo.data.Record.create([
56166     {name:'name',type:'string'},  'value'
56167 ]);
56168
56169
56170 Roo.grid.PropertyStore = function(grid, source){
56171     this.grid = grid;
56172     this.store = new Roo.data.Store({
56173         recordType : Roo.grid.PropertyRecord
56174     });
56175     this.store.on('update', this.onUpdate,  this);
56176     if(source){
56177         this.setSource(source);
56178     }
56179     Roo.grid.PropertyStore.superclass.constructor.call(this);
56180 };
56181
56182
56183
56184 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56185     setSource : function(o){
56186         this.source = o;
56187         this.store.removeAll();
56188         var data = [];
56189         for(var k in o){
56190             if(this.isEditableValue(o[k])){
56191                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56192             }
56193         }
56194         this.store.loadRecords({records: data}, {}, true);
56195     },
56196
56197     onUpdate : function(ds, record, type){
56198         if(type == Roo.data.Record.EDIT){
56199             var v = record.data['value'];
56200             var oldValue = record.modified['value'];
56201             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56202                 this.source[record.id] = v;
56203                 record.commit();
56204                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56205             }else{
56206                 record.reject();
56207             }
56208         }
56209     },
56210
56211     getProperty : function(row){
56212        return this.store.getAt(row);
56213     },
56214
56215     isEditableValue: function(val){
56216         if(val && val instanceof Date){
56217             return true;
56218         }else if(typeof val == 'object' || typeof val == 'function'){
56219             return false;
56220         }
56221         return true;
56222     },
56223
56224     setValue : function(prop, value){
56225         this.source[prop] = value;
56226         this.store.getById(prop).set('value', value);
56227     },
56228
56229     getSource : function(){
56230         return this.source;
56231     }
56232 });
56233
56234 Roo.grid.PropertyColumnModel = function(grid, store){
56235     this.grid = grid;
56236     var g = Roo.grid;
56237     g.PropertyColumnModel.superclass.constructor.call(this, [
56238         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56239         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56240     ]);
56241     this.store = store;
56242     this.bselect = Roo.DomHelper.append(document.body, {
56243         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56244             {tag: 'option', value: 'true', html: 'true'},
56245             {tag: 'option', value: 'false', html: 'false'}
56246         ]
56247     });
56248     Roo.id(this.bselect);
56249     var f = Roo.form;
56250     this.editors = {
56251         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56252         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56253         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56254         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56255         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56256     };
56257     this.renderCellDelegate = this.renderCell.createDelegate(this);
56258     this.renderPropDelegate = this.renderProp.createDelegate(this);
56259 };
56260
56261 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56262     
56263     
56264     nameText : 'Name',
56265     valueText : 'Value',
56266     
56267     dateFormat : 'm/j/Y',
56268     
56269     
56270     renderDate : function(dateVal){
56271         return dateVal.dateFormat(this.dateFormat);
56272     },
56273
56274     renderBool : function(bVal){
56275         return bVal ? 'true' : 'false';
56276     },
56277
56278     isCellEditable : function(colIndex, rowIndex){
56279         return colIndex == 1;
56280     },
56281
56282     getRenderer : function(col){
56283         return col == 1 ?
56284             this.renderCellDelegate : this.renderPropDelegate;
56285     },
56286
56287     renderProp : function(v){
56288         return this.getPropertyName(v);
56289     },
56290
56291     renderCell : function(val){
56292         var rv = val;
56293         if(val instanceof Date){
56294             rv = this.renderDate(val);
56295         }else if(typeof val == 'boolean'){
56296             rv = this.renderBool(val);
56297         }
56298         return Roo.util.Format.htmlEncode(rv);
56299     },
56300
56301     getPropertyName : function(name){
56302         var pn = this.grid.propertyNames;
56303         return pn && pn[name] ? pn[name] : name;
56304     },
56305
56306     getCellEditor : function(colIndex, rowIndex){
56307         var p = this.store.getProperty(rowIndex);
56308         var n = p.data['name'], val = p.data['value'];
56309         
56310         if(typeof(this.grid.customEditors[n]) == 'string'){
56311             return this.editors[this.grid.customEditors[n]];
56312         }
56313         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56314             return this.grid.customEditors[n];
56315         }
56316         if(val instanceof Date){
56317             return this.editors['date'];
56318         }else if(typeof val == 'number'){
56319             return this.editors['number'];
56320         }else if(typeof val == 'boolean'){
56321             return this.editors['boolean'];
56322         }else{
56323             return this.editors['string'];
56324         }
56325     }
56326 });
56327
56328 /**
56329  * @class Roo.grid.PropertyGrid
56330  * @extends Roo.grid.EditorGrid
56331  * This class represents the  interface of a component based property grid control.
56332  * <br><br>Usage:<pre><code>
56333  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56334       
56335  });
56336  // set any options
56337  grid.render();
56338  * </code></pre>
56339   
56340  * @constructor
56341  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56342  * The container MUST have some type of size defined for the grid to fill. The container will be
56343  * automatically set to position relative if it isn't already.
56344  * @param {Object} config A config object that sets properties on this grid.
56345  */
56346 Roo.grid.PropertyGrid = function(container, config){
56347     config = config || {};
56348     var store = new Roo.grid.PropertyStore(this);
56349     this.store = store;
56350     var cm = new Roo.grid.PropertyColumnModel(this, store);
56351     store.store.sort('name', 'ASC');
56352     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56353         ds: store.store,
56354         cm: cm,
56355         enableColLock:false,
56356         enableColumnMove:false,
56357         stripeRows:false,
56358         trackMouseOver: false,
56359         clicksToEdit:1
56360     }, config));
56361     this.getGridEl().addClass('x-props-grid');
56362     this.lastEditRow = null;
56363     this.on('columnresize', this.onColumnResize, this);
56364     this.addEvents({
56365          /**
56366              * @event beforepropertychange
56367              * Fires before a property changes (return false to stop?)
56368              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56369              * @param {String} id Record Id
56370              * @param {String} newval New Value
56371          * @param {String} oldval Old Value
56372              */
56373         "beforepropertychange": true,
56374         /**
56375              * @event propertychange
56376              * Fires after a property changes
56377              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56378              * @param {String} id Record Id
56379              * @param {String} newval New Value
56380          * @param {String} oldval Old Value
56381              */
56382         "propertychange": true
56383     });
56384     this.customEditors = this.customEditors || {};
56385 };
56386 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56387     
56388      /**
56389      * @cfg {Object} customEditors map of colnames=> custom editors.
56390      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56391      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56392      * false disables editing of the field.
56393          */
56394     
56395       /**
56396      * @cfg {Object} propertyNames map of property Names to their displayed value
56397          */
56398     
56399     render : function(){
56400         Roo.grid.PropertyGrid.superclass.render.call(this);
56401         this.autoSize.defer(100, this);
56402     },
56403
56404     autoSize : function(){
56405         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56406         if(this.view){
56407             this.view.fitColumns();
56408         }
56409     },
56410
56411     onColumnResize : function(){
56412         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56413         this.autoSize();
56414     },
56415     /**
56416      * Sets the data for the Grid
56417      * accepts a Key => Value object of all the elements avaiable.
56418      * @param {Object} data  to appear in grid.
56419      */
56420     setSource : function(source){
56421         this.store.setSource(source);
56422         //this.autoSize();
56423     },
56424     /**
56425      * Gets all the data from the grid.
56426      * @return {Object} data  data stored in grid
56427      */
56428     getSource : function(){
56429         return this.store.getSource();
56430     }
56431 });/*
56432   
56433  * Licence LGPL
56434  
56435  */
56436  
56437 /**
56438  * @class Roo.grid.Calendar
56439  * @extends Roo.util.Grid
56440  * This class extends the Grid to provide a calendar widget
56441  * <br><br>Usage:<pre><code>
56442  var grid = new Roo.grid.Calendar("my-container-id", {
56443      ds: myDataStore,
56444      cm: myColModel,
56445      selModel: mySelectionModel,
56446      autoSizeColumns: true,
56447      monitorWindowResize: false,
56448      trackMouseOver: true
56449      eventstore : real data store..
56450  });
56451  // set any options
56452  grid.render();
56453   
56454   * @constructor
56455  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56456  * The container MUST have some type of size defined for the grid to fill. The container will be
56457  * automatically set to position relative if it isn't already.
56458  * @param {Object} config A config object that sets properties on this grid.
56459  */
56460 Roo.grid.Calendar = function(container, config){
56461         // initialize the container
56462         this.container = Roo.get(container);
56463         this.container.update("");
56464         this.container.setStyle("overflow", "hidden");
56465     this.container.addClass('x-grid-container');
56466
56467     this.id = this.container.id;
56468
56469     Roo.apply(this, config);
56470     // check and correct shorthanded configs
56471     
56472     var rows = [];
56473     var d =1;
56474     for (var r = 0;r < 6;r++) {
56475         
56476         rows[r]=[];
56477         for (var c =0;c < 7;c++) {
56478             rows[r][c]= '';
56479         }
56480     }
56481     if (this.eventStore) {
56482         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56483         this.eventStore.on('load',this.onLoad, this);
56484         this.eventStore.on('beforeload',this.clearEvents, this);
56485          
56486     }
56487     
56488     this.dataSource = new Roo.data.Store({
56489             proxy: new Roo.data.MemoryProxy(rows),
56490             reader: new Roo.data.ArrayReader({}, [
56491                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56492     });
56493
56494     this.dataSource.load();
56495     this.ds = this.dataSource;
56496     this.ds.xmodule = this.xmodule || false;
56497     
56498     
56499     var cellRender = function(v,x,r)
56500     {
56501         return String.format(
56502             '<div class="fc-day  fc-widget-content"><div>' +
56503                 '<div class="fc-event-container"></div>' +
56504                 '<div class="fc-day-number">{0}</div>'+
56505                 
56506                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56507             '</div></div>', v);
56508     
56509     }
56510     
56511     
56512     this.colModel = new Roo.grid.ColumnModel( [
56513         {
56514             xtype: 'ColumnModel',
56515             xns: Roo.grid,
56516             dataIndex : 'weekday0',
56517             header : 'Sunday',
56518             renderer : cellRender
56519         },
56520         {
56521             xtype: 'ColumnModel',
56522             xns: Roo.grid,
56523             dataIndex : 'weekday1',
56524             header : 'Monday',
56525             renderer : cellRender
56526         },
56527         {
56528             xtype: 'ColumnModel',
56529             xns: Roo.grid,
56530             dataIndex : 'weekday2',
56531             header : 'Tuesday',
56532             renderer : cellRender
56533         },
56534         {
56535             xtype: 'ColumnModel',
56536             xns: Roo.grid,
56537             dataIndex : 'weekday3',
56538             header : 'Wednesday',
56539             renderer : cellRender
56540         },
56541         {
56542             xtype: 'ColumnModel',
56543             xns: Roo.grid,
56544             dataIndex : 'weekday4',
56545             header : 'Thursday',
56546             renderer : cellRender
56547         },
56548         {
56549             xtype: 'ColumnModel',
56550             xns: Roo.grid,
56551             dataIndex : 'weekday5',
56552             header : 'Friday',
56553             renderer : cellRender
56554         },
56555         {
56556             xtype: 'ColumnModel',
56557             xns: Roo.grid,
56558             dataIndex : 'weekday6',
56559             header : 'Saturday',
56560             renderer : cellRender
56561         }
56562     ]);
56563     this.cm = this.colModel;
56564     this.cm.xmodule = this.xmodule || false;
56565  
56566         
56567           
56568     //this.selModel = new Roo.grid.CellSelectionModel();
56569     //this.sm = this.selModel;
56570     //this.selModel.init(this);
56571     
56572     
56573     if(this.width){
56574         this.container.setWidth(this.width);
56575     }
56576
56577     if(this.height){
56578         this.container.setHeight(this.height);
56579     }
56580     /** @private */
56581         this.addEvents({
56582         // raw events
56583         /**
56584          * @event click
56585          * The raw click event for the entire grid.
56586          * @param {Roo.EventObject} e
56587          */
56588         "click" : true,
56589         /**
56590          * @event dblclick
56591          * The raw dblclick event for the entire grid.
56592          * @param {Roo.EventObject} e
56593          */
56594         "dblclick" : true,
56595         /**
56596          * @event contextmenu
56597          * The raw contextmenu event for the entire grid.
56598          * @param {Roo.EventObject} e
56599          */
56600         "contextmenu" : true,
56601         /**
56602          * @event mousedown
56603          * The raw mousedown event for the entire grid.
56604          * @param {Roo.EventObject} e
56605          */
56606         "mousedown" : true,
56607         /**
56608          * @event mouseup
56609          * The raw mouseup event for the entire grid.
56610          * @param {Roo.EventObject} e
56611          */
56612         "mouseup" : true,
56613         /**
56614          * @event mouseover
56615          * The raw mouseover event for the entire grid.
56616          * @param {Roo.EventObject} e
56617          */
56618         "mouseover" : true,
56619         /**
56620          * @event mouseout
56621          * The raw mouseout event for the entire grid.
56622          * @param {Roo.EventObject} e
56623          */
56624         "mouseout" : true,
56625         /**
56626          * @event keypress
56627          * The raw keypress event for the entire grid.
56628          * @param {Roo.EventObject} e
56629          */
56630         "keypress" : true,
56631         /**
56632          * @event keydown
56633          * The raw keydown event for the entire grid.
56634          * @param {Roo.EventObject} e
56635          */
56636         "keydown" : true,
56637
56638         // custom events
56639
56640         /**
56641          * @event cellclick
56642          * Fires when a cell is clicked
56643          * @param {Grid} this
56644          * @param {Number} rowIndex
56645          * @param {Number} columnIndex
56646          * @param {Roo.EventObject} e
56647          */
56648         "cellclick" : true,
56649         /**
56650          * @event celldblclick
56651          * Fires when a cell is double clicked
56652          * @param {Grid} this
56653          * @param {Number} rowIndex
56654          * @param {Number} columnIndex
56655          * @param {Roo.EventObject} e
56656          */
56657         "celldblclick" : true,
56658         /**
56659          * @event rowclick
56660          * Fires when a row is clicked
56661          * @param {Grid} this
56662          * @param {Number} rowIndex
56663          * @param {Roo.EventObject} e
56664          */
56665         "rowclick" : true,
56666         /**
56667          * @event rowdblclick
56668          * Fires when a row is double clicked
56669          * @param {Grid} this
56670          * @param {Number} rowIndex
56671          * @param {Roo.EventObject} e
56672          */
56673         "rowdblclick" : true,
56674         /**
56675          * @event headerclick
56676          * Fires when a header is clicked
56677          * @param {Grid} this
56678          * @param {Number} columnIndex
56679          * @param {Roo.EventObject} e
56680          */
56681         "headerclick" : true,
56682         /**
56683          * @event headerdblclick
56684          * Fires when a header cell is double clicked
56685          * @param {Grid} this
56686          * @param {Number} columnIndex
56687          * @param {Roo.EventObject} e
56688          */
56689         "headerdblclick" : true,
56690         /**
56691          * @event rowcontextmenu
56692          * Fires when a row is right clicked
56693          * @param {Grid} this
56694          * @param {Number} rowIndex
56695          * @param {Roo.EventObject} e
56696          */
56697         "rowcontextmenu" : true,
56698         /**
56699          * @event cellcontextmenu
56700          * Fires when a cell is right clicked
56701          * @param {Grid} this
56702          * @param {Number} rowIndex
56703          * @param {Number} cellIndex
56704          * @param {Roo.EventObject} e
56705          */
56706          "cellcontextmenu" : true,
56707         /**
56708          * @event headercontextmenu
56709          * Fires when a header is right clicked
56710          * @param {Grid} this
56711          * @param {Number} columnIndex
56712          * @param {Roo.EventObject} e
56713          */
56714         "headercontextmenu" : true,
56715         /**
56716          * @event bodyscroll
56717          * Fires when the body element is scrolled
56718          * @param {Number} scrollLeft
56719          * @param {Number} scrollTop
56720          */
56721         "bodyscroll" : true,
56722         /**
56723          * @event columnresize
56724          * Fires when the user resizes a column
56725          * @param {Number} columnIndex
56726          * @param {Number} newSize
56727          */
56728         "columnresize" : true,
56729         /**
56730          * @event columnmove
56731          * Fires when the user moves a column
56732          * @param {Number} oldIndex
56733          * @param {Number} newIndex
56734          */
56735         "columnmove" : true,
56736         /**
56737          * @event startdrag
56738          * Fires when row(s) start being dragged
56739          * @param {Grid} this
56740          * @param {Roo.GridDD} dd The drag drop object
56741          * @param {event} e The raw browser event
56742          */
56743         "startdrag" : true,
56744         /**
56745          * @event enddrag
56746          * Fires when a drag operation is complete
56747          * @param {Grid} this
56748          * @param {Roo.GridDD} dd The drag drop object
56749          * @param {event} e The raw browser event
56750          */
56751         "enddrag" : true,
56752         /**
56753          * @event dragdrop
56754          * Fires when dragged row(s) are dropped on a valid DD target
56755          * @param {Grid} this
56756          * @param {Roo.GridDD} dd The drag drop object
56757          * @param {String} targetId The target drag drop object
56758          * @param {event} e The raw browser event
56759          */
56760         "dragdrop" : true,
56761         /**
56762          * @event dragover
56763          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56764          * @param {Grid} this
56765          * @param {Roo.GridDD} dd The drag drop object
56766          * @param {String} targetId The target drag drop object
56767          * @param {event} e The raw browser event
56768          */
56769         "dragover" : true,
56770         /**
56771          * @event dragenter
56772          *  Fires when the dragged row(s) first cross another DD target while being dragged
56773          * @param {Grid} this
56774          * @param {Roo.GridDD} dd The drag drop object
56775          * @param {String} targetId The target drag drop object
56776          * @param {event} e The raw browser event
56777          */
56778         "dragenter" : true,
56779         /**
56780          * @event dragout
56781          * Fires when the dragged row(s) leave another DD target while being dragged
56782          * @param {Grid} this
56783          * @param {Roo.GridDD} dd The drag drop object
56784          * @param {String} targetId The target drag drop object
56785          * @param {event} e The raw browser event
56786          */
56787         "dragout" : true,
56788         /**
56789          * @event rowclass
56790          * Fires when a row is rendered, so you can change add a style to it.
56791          * @param {GridView} gridview   The grid view
56792          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56793          */
56794         'rowclass' : true,
56795
56796         /**
56797          * @event render
56798          * Fires when the grid is rendered
56799          * @param {Grid} grid
56800          */
56801         'render' : true,
56802             /**
56803              * @event select
56804              * Fires when a date is selected
56805              * @param {DatePicker} this
56806              * @param {Date} date The selected date
56807              */
56808         'select': true,
56809         /**
56810              * @event monthchange
56811              * Fires when the displayed month changes 
56812              * @param {DatePicker} this
56813              * @param {Date} date The selected month
56814              */
56815         'monthchange': true,
56816         /**
56817              * @event evententer
56818              * Fires when mouse over an event
56819              * @param {Calendar} this
56820              * @param {event} Event
56821              */
56822         'evententer': true,
56823         /**
56824              * @event eventleave
56825              * Fires when the mouse leaves an
56826              * @param {Calendar} this
56827              * @param {event}
56828              */
56829         'eventleave': true,
56830         /**
56831              * @event eventclick
56832              * Fires when the mouse click an
56833              * @param {Calendar} this
56834              * @param {event}
56835              */
56836         'eventclick': true,
56837         /**
56838              * @event eventrender
56839              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56840              * @param {Calendar} this
56841              * @param {data} data to be modified
56842              */
56843         'eventrender': true
56844         
56845     });
56846
56847     Roo.grid.Grid.superclass.constructor.call(this);
56848     this.on('render', function() {
56849         this.view.el.addClass('x-grid-cal'); 
56850         
56851         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56852
56853     },this);
56854     
56855     if (!Roo.grid.Calendar.style) {
56856         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56857             
56858             
56859             '.x-grid-cal .x-grid-col' :  {
56860                 height: 'auto !important',
56861                 'vertical-align': 'top'
56862             },
56863             '.x-grid-cal  .fc-event-hori' : {
56864                 height: '14px'
56865             }
56866              
56867             
56868         }, Roo.id());
56869     }
56870
56871     
56872     
56873 };
56874 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56875     /**
56876      * @cfg {Store} eventStore The store that loads events.
56877      */
56878     eventStore : 25,
56879
56880      
56881     activeDate : false,
56882     startDay : 0,
56883     autoWidth : true,
56884     monitorWindowResize : false,
56885
56886     
56887     resizeColumns : function() {
56888         var col = (this.view.el.getWidth() / 7) - 3;
56889         // loop through cols, and setWidth
56890         for(var i =0 ; i < 7 ; i++){
56891             this.cm.setColumnWidth(i, col);
56892         }
56893     },
56894      setDate :function(date) {
56895         
56896         Roo.log('setDate?');
56897         
56898         this.resizeColumns();
56899         var vd = this.activeDate;
56900         this.activeDate = date;
56901 //        if(vd && this.el){
56902 //            var t = date.getTime();
56903 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56904 //                Roo.log('using add remove');
56905 //                
56906 //                this.fireEvent('monthchange', this, date);
56907 //                
56908 //                this.cells.removeClass("fc-state-highlight");
56909 //                this.cells.each(function(c){
56910 //                   if(c.dateValue == t){
56911 //                       c.addClass("fc-state-highlight");
56912 //                       setTimeout(function(){
56913 //                            try{c.dom.firstChild.focus();}catch(e){}
56914 //                       }, 50);
56915 //                       return false;
56916 //                   }
56917 //                   return true;
56918 //                });
56919 //                return;
56920 //            }
56921 //        }
56922         
56923         var days = date.getDaysInMonth();
56924         
56925         var firstOfMonth = date.getFirstDateOfMonth();
56926         var startingPos = firstOfMonth.getDay()-this.startDay;
56927         
56928         if(startingPos < this.startDay){
56929             startingPos += 7;
56930         }
56931         
56932         var pm = date.add(Date.MONTH, -1);
56933         var prevStart = pm.getDaysInMonth()-startingPos;
56934 //        
56935         
56936         
56937         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56938         
56939         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56940         //this.cells.addClassOnOver('fc-state-hover');
56941         
56942         var cells = this.cells.elements;
56943         var textEls = this.textNodes;
56944         
56945         //Roo.each(cells, function(cell){
56946         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56947         //});
56948         
56949         days += startingPos;
56950
56951         // convert everything to numbers so it's fast
56952         var day = 86400000;
56953         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56954         //Roo.log(d);
56955         //Roo.log(pm);
56956         //Roo.log(prevStart);
56957         
56958         var today = new Date().clearTime().getTime();
56959         var sel = date.clearTime().getTime();
56960         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56961         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56962         var ddMatch = this.disabledDatesRE;
56963         var ddText = this.disabledDatesText;
56964         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56965         var ddaysText = this.disabledDaysText;
56966         var format = this.format;
56967         
56968         var setCellClass = function(cal, cell){
56969             
56970             //Roo.log('set Cell Class');
56971             cell.title = "";
56972             var t = d.getTime();
56973             
56974             //Roo.log(d);
56975             
56976             
56977             cell.dateValue = t;
56978             if(t == today){
56979                 cell.className += " fc-today";
56980                 cell.className += " fc-state-highlight";
56981                 cell.title = cal.todayText;
56982             }
56983             if(t == sel){
56984                 // disable highlight in other month..
56985                 cell.className += " fc-state-highlight";
56986                 
56987             }
56988             // disabling
56989             if(t < min) {
56990                 //cell.className = " fc-state-disabled";
56991                 cell.title = cal.minText;
56992                 return;
56993             }
56994             if(t > max) {
56995                 //cell.className = " fc-state-disabled";
56996                 cell.title = cal.maxText;
56997                 return;
56998             }
56999             if(ddays){
57000                 if(ddays.indexOf(d.getDay()) != -1){
57001                     // cell.title = ddaysText;
57002                    // cell.className = " fc-state-disabled";
57003                 }
57004             }
57005             if(ddMatch && format){
57006                 var fvalue = d.dateFormat(format);
57007                 if(ddMatch.test(fvalue)){
57008                     cell.title = ddText.replace("%0", fvalue);
57009                    cell.className = " fc-state-disabled";
57010                 }
57011             }
57012             
57013             if (!cell.initialClassName) {
57014                 cell.initialClassName = cell.dom.className;
57015             }
57016             
57017             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57018         };
57019
57020         var i = 0;
57021         
57022         for(; i < startingPos; i++) {
57023             cells[i].dayName =  (++prevStart);
57024             Roo.log(textEls[i]);
57025             d.setDate(d.getDate()+1);
57026             
57027             //cells[i].className = "fc-past fc-other-month";
57028             setCellClass(this, cells[i]);
57029         }
57030         
57031         var intDay = 0;
57032         
57033         for(; i < days; i++){
57034             intDay = i - startingPos + 1;
57035             cells[i].dayName =  (intDay);
57036             d.setDate(d.getDate()+1);
57037             
57038             cells[i].className = ''; // "x-date-active";
57039             setCellClass(this, cells[i]);
57040         }
57041         var extraDays = 0;
57042         
57043         for(; i < 42; i++) {
57044             //textEls[i].innerHTML = (++extraDays);
57045             
57046             d.setDate(d.getDate()+1);
57047             cells[i].dayName = (++extraDays);
57048             cells[i].className = "fc-future fc-other-month";
57049             setCellClass(this, cells[i]);
57050         }
57051         
57052         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57053         
57054         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57055         
57056         // this will cause all the cells to mis
57057         var rows= [];
57058         var i =0;
57059         for (var r = 0;r < 6;r++) {
57060             for (var c =0;c < 7;c++) {
57061                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57062             }    
57063         }
57064         
57065         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57066         for(i=0;i<cells.length;i++) {
57067             
57068             this.cells.elements[i].dayName = cells[i].dayName ;
57069             this.cells.elements[i].className = cells[i].className;
57070             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57071             this.cells.elements[i].title = cells[i].title ;
57072             this.cells.elements[i].dateValue = cells[i].dateValue ;
57073         }
57074         
57075         
57076         
57077         
57078         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57079         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57080         
57081         ////if(totalRows != 6){
57082             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57083            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57084        // }
57085         
57086         this.fireEvent('monthchange', this, date);
57087         
57088         
57089     },
57090  /**
57091      * Returns the grid's SelectionModel.
57092      * @return {SelectionModel}
57093      */
57094     getSelectionModel : function(){
57095         if(!this.selModel){
57096             this.selModel = new Roo.grid.CellSelectionModel();
57097         }
57098         return this.selModel;
57099     },
57100
57101     load: function() {
57102         this.eventStore.load()
57103         
57104         
57105         
57106     },
57107     
57108     findCell : function(dt) {
57109         dt = dt.clearTime().getTime();
57110         var ret = false;
57111         this.cells.each(function(c){
57112             //Roo.log("check " +c.dateValue + '?=' + dt);
57113             if(c.dateValue == dt){
57114                 ret = c;
57115                 return false;
57116             }
57117             return true;
57118         });
57119         
57120         return ret;
57121     },
57122     
57123     findCells : function(rec) {
57124         var s = rec.data.start_dt.clone().clearTime().getTime();
57125        // Roo.log(s);
57126         var e= rec.data.end_dt.clone().clearTime().getTime();
57127        // Roo.log(e);
57128         var ret = [];
57129         this.cells.each(function(c){
57130              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57131             
57132             if(c.dateValue > e){
57133                 return ;
57134             }
57135             if(c.dateValue < s){
57136                 return ;
57137             }
57138             ret.push(c);
57139         });
57140         
57141         return ret;    
57142     },
57143     
57144     findBestRow: function(cells)
57145     {
57146         var ret = 0;
57147         
57148         for (var i =0 ; i < cells.length;i++) {
57149             ret  = Math.max(cells[i].rows || 0,ret);
57150         }
57151         return ret;
57152         
57153     },
57154     
57155     
57156     addItem : function(rec)
57157     {
57158         // look for vertical location slot in
57159         var cells = this.findCells(rec);
57160         
57161         rec.row = this.findBestRow(cells);
57162         
57163         // work out the location.
57164         
57165         var crow = false;
57166         var rows = [];
57167         for(var i =0; i < cells.length; i++) {
57168             if (!crow) {
57169                 crow = {
57170                     start : cells[i],
57171                     end :  cells[i]
57172                 };
57173                 continue;
57174             }
57175             if (crow.start.getY() == cells[i].getY()) {
57176                 // on same row.
57177                 crow.end = cells[i];
57178                 continue;
57179             }
57180             // different row.
57181             rows.push(crow);
57182             crow = {
57183                 start: cells[i],
57184                 end : cells[i]
57185             };
57186             
57187         }
57188         
57189         rows.push(crow);
57190         rec.els = [];
57191         rec.rows = rows;
57192         rec.cells = cells;
57193         for (var i = 0; i < cells.length;i++) {
57194             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57195             
57196         }
57197         
57198         
57199     },
57200     
57201     clearEvents: function() {
57202         
57203         if (!this.eventStore.getCount()) {
57204             return;
57205         }
57206         // reset number of rows in cells.
57207         Roo.each(this.cells.elements, function(c){
57208             c.rows = 0;
57209         });
57210         
57211         this.eventStore.each(function(e) {
57212             this.clearEvent(e);
57213         },this);
57214         
57215     },
57216     
57217     clearEvent : function(ev)
57218     {
57219         if (ev.els) {
57220             Roo.each(ev.els, function(el) {
57221                 el.un('mouseenter' ,this.onEventEnter, this);
57222                 el.un('mouseleave' ,this.onEventLeave, this);
57223                 el.remove();
57224             },this);
57225             ev.els = [];
57226         }
57227     },
57228     
57229     
57230     renderEvent : function(ev,ctr) {
57231         if (!ctr) {
57232              ctr = this.view.el.select('.fc-event-container',true).first();
57233         }
57234         
57235          
57236         this.clearEvent(ev);
57237             //code
57238        
57239         
57240         
57241         ev.els = [];
57242         var cells = ev.cells;
57243         var rows = ev.rows;
57244         this.fireEvent('eventrender', this, ev);
57245         
57246         for(var i =0; i < rows.length; i++) {
57247             
57248             cls = '';
57249             if (i == 0) {
57250                 cls += ' fc-event-start';
57251             }
57252             if ((i+1) == rows.length) {
57253                 cls += ' fc-event-end';
57254             }
57255             
57256             //Roo.log(ev.data);
57257             // how many rows should it span..
57258             var cg = this.eventTmpl.append(ctr,Roo.apply({
57259                 fccls : cls
57260                 
57261             }, ev.data) , true);
57262             
57263             
57264             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57265             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57266             cg.on('click', this.onEventClick, this, ev);
57267             
57268             ev.els.push(cg);
57269             
57270             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57271             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57272             //Roo.log(cg);
57273              
57274             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57275             cg.setWidth(ebox.right - sbox.x -2);
57276         }
57277     },
57278     
57279     renderEvents: function()
57280     {   
57281         // first make sure there is enough space..
57282         
57283         if (!this.eventTmpl) {
57284             this.eventTmpl = new Roo.Template(
57285                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57286                     '<div class="fc-event-inner">' +
57287                         '<span class="fc-event-time">{time}</span>' +
57288                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57289                     '</div>' +
57290                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57291                 '</div>'
57292             );
57293                 
57294         }
57295                
57296         
57297         
57298         this.cells.each(function(c) {
57299             //Roo.log(c.select('.fc-day-content div',true).first());
57300             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57301         });
57302         
57303         var ctr = this.view.el.select('.fc-event-container',true).first();
57304         
57305         var cls;
57306         this.eventStore.each(function(ev){
57307             
57308             this.renderEvent(ev);
57309              
57310              
57311         }, this);
57312         this.view.layout();
57313         
57314     },
57315     
57316     onEventEnter: function (e, el,event,d) {
57317         this.fireEvent('evententer', this, el, event);
57318     },
57319     
57320     onEventLeave: function (e, el,event,d) {
57321         this.fireEvent('eventleave', this, el, event);
57322     },
57323     
57324     onEventClick: function (e, el,event,d) {
57325         this.fireEvent('eventclick', this, el, event);
57326     },
57327     
57328     onMonthChange: function () {
57329         this.store.load();
57330     },
57331     
57332     onLoad: function () {
57333         
57334         //Roo.log('calendar onload');
57335 //         
57336         if(this.eventStore.getCount() > 0){
57337             
57338            
57339             
57340             this.eventStore.each(function(d){
57341                 
57342                 
57343                 // FIXME..
57344                 var add =   d.data;
57345                 if (typeof(add.end_dt) == 'undefined')  {
57346                     Roo.log("Missing End time in calendar data: ");
57347                     Roo.log(d);
57348                     return;
57349                 }
57350                 if (typeof(add.start_dt) == 'undefined')  {
57351                     Roo.log("Missing Start time in calendar data: ");
57352                     Roo.log(d);
57353                     return;
57354                 }
57355                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57356                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57357                 add.id = add.id || d.id;
57358                 add.title = add.title || '??';
57359                 
57360                 this.addItem(d);
57361                 
57362              
57363             },this);
57364         }
57365         
57366         this.renderEvents();
57367     }
57368     
57369
57370 });
57371 /*
57372  grid : {
57373                 xtype: 'Grid',
57374                 xns: Roo.grid,
57375                 listeners : {
57376                     render : function ()
57377                     {
57378                         _this.grid = this;
57379                         
57380                         if (!this.view.el.hasClass('course-timesheet')) {
57381                             this.view.el.addClass('course-timesheet');
57382                         }
57383                         if (this.tsStyle) {
57384                             this.ds.load({});
57385                             return; 
57386                         }
57387                         Roo.log('width');
57388                         Roo.log(_this.grid.view.el.getWidth());
57389                         
57390                         
57391                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57392                             '.course-timesheet .x-grid-row' : {
57393                                 height: '80px'
57394                             },
57395                             '.x-grid-row td' : {
57396                                 'vertical-align' : 0
57397                             },
57398                             '.course-edit-link' : {
57399                                 'color' : 'blue',
57400                                 'text-overflow' : 'ellipsis',
57401                                 'overflow' : 'hidden',
57402                                 'white-space' : 'nowrap',
57403                                 'cursor' : 'pointer'
57404                             },
57405                             '.sub-link' : {
57406                                 'color' : 'green'
57407                             },
57408                             '.de-act-sup-link' : {
57409                                 'color' : 'purple',
57410                                 'text-decoration' : 'line-through'
57411                             },
57412                             '.de-act-link' : {
57413                                 'color' : 'red',
57414                                 'text-decoration' : 'line-through'
57415                             },
57416                             '.course-timesheet .course-highlight' : {
57417                                 'border-top-style': 'dashed !important',
57418                                 'border-bottom-bottom': 'dashed !important'
57419                             },
57420                             '.course-timesheet .course-item' : {
57421                                 'font-family'   : 'tahoma, arial, helvetica',
57422                                 'font-size'     : '11px',
57423                                 'overflow'      : 'hidden',
57424                                 'padding-left'  : '10px',
57425                                 'padding-right' : '10px',
57426                                 'padding-top' : '10px' 
57427                             }
57428                             
57429                         }, Roo.id());
57430                                 this.ds.load({});
57431                     }
57432                 },
57433                 autoWidth : true,
57434                 monitorWindowResize : false,
57435                 cellrenderer : function(v,x,r)
57436                 {
57437                     return v;
57438                 },
57439                 sm : {
57440                     xtype: 'CellSelectionModel',
57441                     xns: Roo.grid
57442                 },
57443                 dataSource : {
57444                     xtype: 'Store',
57445                     xns: Roo.data,
57446                     listeners : {
57447                         beforeload : function (_self, options)
57448                         {
57449                             options.params = options.params || {};
57450                             options.params._month = _this.monthField.getValue();
57451                             options.params.limit = 9999;
57452                             options.params['sort'] = 'when_dt';    
57453                             options.params['dir'] = 'ASC';    
57454                             this.proxy.loadResponse = this.loadResponse;
57455                             Roo.log("load?");
57456                             //this.addColumns();
57457                         },
57458                         load : function (_self, records, options)
57459                         {
57460                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57461                                 // if you click on the translation.. you can edit it...
57462                                 var el = Roo.get(this);
57463                                 var id = el.dom.getAttribute('data-id');
57464                                 var d = el.dom.getAttribute('data-date');
57465                                 var t = el.dom.getAttribute('data-time');
57466                                 //var id = this.child('span').dom.textContent;
57467                                 
57468                                 //Roo.log(this);
57469                                 Pman.Dialog.CourseCalendar.show({
57470                                     id : id,
57471                                     when_d : d,
57472                                     when_t : t,
57473                                     productitem_active : id ? 1 : 0
57474                                 }, function() {
57475                                     _this.grid.ds.load({});
57476                                 });
57477                            
57478                            });
57479                            
57480                            _this.panel.fireEvent('resize', [ '', '' ]);
57481                         }
57482                     },
57483                     loadResponse : function(o, success, response){
57484                             // this is overridden on before load..
57485                             
57486                             Roo.log("our code?");       
57487                             //Roo.log(success);
57488                             //Roo.log(response)
57489                             delete this.activeRequest;
57490                             if(!success){
57491                                 this.fireEvent("loadexception", this, o, response);
57492                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57493                                 return;
57494                             }
57495                             var result;
57496                             try {
57497                                 result = o.reader.read(response);
57498                             }catch(e){
57499                                 Roo.log("load exception?");
57500                                 this.fireEvent("loadexception", this, o, response, e);
57501                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57502                                 return;
57503                             }
57504                             Roo.log("ready...");        
57505                             // loop through result.records;
57506                             // and set this.tdate[date] = [] << array of records..
57507                             _this.tdata  = {};
57508                             Roo.each(result.records, function(r){
57509                                 //Roo.log(r.data);
57510                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57511                                     _this.tdata[r.data.when_dt.format('j')] = [];
57512                                 }
57513                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57514                             });
57515                             
57516                             //Roo.log(_this.tdata);
57517                             
57518                             result.records = [];
57519                             result.totalRecords = 6;
57520                     
57521                             // let's generate some duumy records for the rows.
57522                             //var st = _this.dateField.getValue();
57523                             
57524                             // work out monday..
57525                             //st = st.add(Date.DAY, -1 * st.format('w'));
57526                             
57527                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57528                             
57529                             var firstOfMonth = date.getFirstDayOfMonth();
57530                             var days = date.getDaysInMonth();
57531                             var d = 1;
57532                             var firstAdded = false;
57533                             for (var i = 0; i < result.totalRecords ; i++) {
57534                                 //var d= st.add(Date.DAY, i);
57535                                 var row = {};
57536                                 var added = 0;
57537                                 for(var w = 0 ; w < 7 ; w++){
57538                                     if(!firstAdded && firstOfMonth != w){
57539                                         continue;
57540                                     }
57541                                     if(d > days){
57542                                         continue;
57543                                     }
57544                                     firstAdded = true;
57545                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57546                                     row['weekday'+w] = String.format(
57547                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57548                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57549                                                     d,
57550                                                     date.format('Y-m-')+dd
57551                                                 );
57552                                     added++;
57553                                     if(typeof(_this.tdata[d]) != 'undefined'){
57554                                         Roo.each(_this.tdata[d], function(r){
57555                                             var is_sub = '';
57556                                             var deactive = '';
57557                                             var id = r.id;
57558                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57559                                             if(r.parent_id*1>0){
57560                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57561                                                 id = r.parent_id;
57562                                             }
57563                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57564                                                 deactive = 'de-act-link';
57565                                             }
57566                                             
57567                                             row['weekday'+w] += String.format(
57568                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57569                                                     id, //0
57570                                                     r.product_id_name, //1
57571                                                     r.when_dt.format('h:ia'), //2
57572                                                     is_sub, //3
57573                                                     deactive, //4
57574                                                     desc // 5
57575                                             );
57576                                         });
57577                                     }
57578                                     d++;
57579                                 }
57580                                 
57581                                 // only do this if something added..
57582                                 if(added > 0){ 
57583                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57584                                 }
57585                                 
57586                                 
57587                                 // push it twice. (second one with an hour..
57588                                 
57589                             }
57590                             //Roo.log(result);
57591                             this.fireEvent("load", this, o, o.request.arg);
57592                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57593                         },
57594                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57595                     proxy : {
57596                         xtype: 'HttpProxy',
57597                         xns: Roo.data,
57598                         method : 'GET',
57599                         url : baseURL + '/Roo/Shop_course.php'
57600                     },
57601                     reader : {
57602                         xtype: 'JsonReader',
57603                         xns: Roo.data,
57604                         id : 'id',
57605                         fields : [
57606                             {
57607                                 'name': 'id',
57608                                 'type': 'int'
57609                             },
57610                             {
57611                                 'name': 'when_dt',
57612                                 'type': 'string'
57613                             },
57614                             {
57615                                 'name': 'end_dt',
57616                                 'type': 'string'
57617                             },
57618                             {
57619                                 'name': 'parent_id',
57620                                 'type': 'int'
57621                             },
57622                             {
57623                                 'name': 'product_id',
57624                                 'type': 'int'
57625                             },
57626                             {
57627                                 'name': 'productitem_id',
57628                                 'type': 'int'
57629                             },
57630                             {
57631                                 'name': 'guid',
57632                                 'type': 'int'
57633                             }
57634                         ]
57635                     }
57636                 },
57637                 toolbar : {
57638                     xtype: 'Toolbar',
57639                     xns: Roo,
57640                     items : [
57641                         {
57642                             xtype: 'Button',
57643                             xns: Roo.Toolbar,
57644                             listeners : {
57645                                 click : function (_self, e)
57646                                 {
57647                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57648                                     sd.setMonth(sd.getMonth()-1);
57649                                     _this.monthField.setValue(sd.format('Y-m-d'));
57650                                     _this.grid.ds.load({});
57651                                 }
57652                             },
57653                             text : "Back"
57654                         },
57655                         {
57656                             xtype: 'Separator',
57657                             xns: Roo.Toolbar
57658                         },
57659                         {
57660                             xtype: 'MonthField',
57661                             xns: Roo.form,
57662                             listeners : {
57663                                 render : function (_self)
57664                                 {
57665                                     _this.monthField = _self;
57666                                    // _this.monthField.set  today
57667                                 },
57668                                 select : function (combo, date)
57669                                 {
57670                                     _this.grid.ds.load({});
57671                                 }
57672                             },
57673                             value : (function() { return new Date(); })()
57674                         },
57675                         {
57676                             xtype: 'Separator',
57677                             xns: Roo.Toolbar
57678                         },
57679                         {
57680                             xtype: 'TextItem',
57681                             xns: Roo.Toolbar,
57682                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57683                         },
57684                         {
57685                             xtype: 'Fill',
57686                             xns: Roo.Toolbar
57687                         },
57688                         {
57689                             xtype: 'Button',
57690                             xns: Roo.Toolbar,
57691                             listeners : {
57692                                 click : function (_self, e)
57693                                 {
57694                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57695                                     sd.setMonth(sd.getMonth()+1);
57696                                     _this.monthField.setValue(sd.format('Y-m-d'));
57697                                     _this.grid.ds.load({});
57698                                 }
57699                             },
57700                             text : "Next"
57701                         }
57702                     ]
57703                 },
57704                  
57705             }
57706         };
57707         
57708         *//*
57709  * Based on:
57710  * Ext JS Library 1.1.1
57711  * Copyright(c) 2006-2007, Ext JS, LLC.
57712  *
57713  * Originally Released Under LGPL - original licence link has changed is not relivant.
57714  *
57715  * Fork - LGPL
57716  * <script type="text/javascript">
57717  */
57718  
57719 /**
57720  * @class Roo.LoadMask
57721  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57722  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57723  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57724  * element's UpdateManager load indicator and will be destroyed after the initial load.
57725  * @constructor
57726  * Create a new LoadMask
57727  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57728  * @param {Object} config The config object
57729  */
57730 Roo.LoadMask = function(el, config){
57731     this.el = Roo.get(el);
57732     Roo.apply(this, config);
57733     if(this.store){
57734         this.store.on('beforeload', this.onBeforeLoad, this);
57735         this.store.on('load', this.onLoad, this);
57736         this.store.on('loadexception', this.onLoadException, this);
57737         this.removeMask = false;
57738     }else{
57739         var um = this.el.getUpdateManager();
57740         um.showLoadIndicator = false; // disable the default indicator
57741         um.on('beforeupdate', this.onBeforeLoad, this);
57742         um.on('update', this.onLoad, this);
57743         um.on('failure', this.onLoad, this);
57744         this.removeMask = true;
57745     }
57746 };
57747
57748 Roo.LoadMask.prototype = {
57749     /**
57750      * @cfg {Boolean} removeMask
57751      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57752      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57753      */
57754     /**
57755      * @cfg {String} msg
57756      * The text to display in a centered loading message box (defaults to 'Loading...')
57757      */
57758     msg : 'Loading...',
57759     /**
57760      * @cfg {String} msgCls
57761      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57762      */
57763     msgCls : 'x-mask-loading',
57764
57765     /**
57766      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57767      * @type Boolean
57768      */
57769     disabled: false,
57770
57771     /**
57772      * Disables the mask to prevent it from being displayed
57773      */
57774     disable : function(){
57775        this.disabled = true;
57776     },
57777
57778     /**
57779      * Enables the mask so that it can be displayed
57780      */
57781     enable : function(){
57782         this.disabled = false;
57783     },
57784     
57785     onLoadException : function()
57786     {
57787         Roo.log(arguments);
57788         
57789         if (typeof(arguments[3]) != 'undefined') {
57790             Roo.MessageBox.alert("Error loading",arguments[3]);
57791         } 
57792         /*
57793         try {
57794             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57795                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57796             }   
57797         } catch(e) {
57798             
57799         }
57800         */
57801     
57802         
57803         
57804         this.el.unmask(this.removeMask);
57805     },
57806     // private
57807     onLoad : function()
57808     {
57809         this.el.unmask(this.removeMask);
57810     },
57811
57812     // private
57813     onBeforeLoad : function(){
57814         if(!this.disabled){
57815             this.el.mask(this.msg, this.msgCls);
57816         }
57817     },
57818
57819     // private
57820     destroy : function(){
57821         if(this.store){
57822             this.store.un('beforeload', this.onBeforeLoad, this);
57823             this.store.un('load', this.onLoad, this);
57824             this.store.un('loadexception', this.onLoadException, this);
57825         }else{
57826             var um = this.el.getUpdateManager();
57827             um.un('beforeupdate', this.onBeforeLoad, this);
57828             um.un('update', this.onLoad, this);
57829             um.un('failure', this.onLoad, this);
57830         }
57831     }
57832 };/*
57833  * Based on:
57834  * Ext JS Library 1.1.1
57835  * Copyright(c) 2006-2007, Ext JS, LLC.
57836  *
57837  * Originally Released Under LGPL - original licence link has changed is not relivant.
57838  *
57839  * Fork - LGPL
57840  * <script type="text/javascript">
57841  */
57842
57843
57844 /**
57845  * @class Roo.XTemplate
57846  * @extends Roo.Template
57847  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57848 <pre><code>
57849 var t = new Roo.XTemplate(
57850         '&lt;select name="{name}"&gt;',
57851                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57852         '&lt;/select&gt;'
57853 );
57854  
57855 // then append, applying the master template values
57856  </code></pre>
57857  *
57858  * Supported features:
57859  *
57860  *  Tags:
57861
57862 <pre><code>
57863       {a_variable} - output encoded.
57864       {a_variable.format:("Y-m-d")} - call a method on the variable
57865       {a_variable:raw} - unencoded output
57866       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57867       {a_variable:this.method_on_template(...)} - call a method on the template object.
57868  
57869 </code></pre>
57870  *  The tpl tag:
57871 <pre><code>
57872         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57873         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57874         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57875         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57876   
57877         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57878         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57879 </code></pre>
57880  *      
57881  */
57882 Roo.XTemplate = function()
57883 {
57884     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57885     if (this.html) {
57886         this.compile();
57887     }
57888 };
57889
57890
57891 Roo.extend(Roo.XTemplate, Roo.Template, {
57892
57893     /**
57894      * The various sub templates
57895      */
57896     tpls : false,
57897     /**
57898      *
57899      * basic tag replacing syntax
57900      * WORD:WORD()
57901      *
57902      * // you can fake an object call by doing this
57903      *  x.t:(test,tesT) 
57904      * 
57905      */
57906     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57907
57908     /**
57909      * compile the template
57910      *
57911      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57912      *
57913      */
57914     compile: function()
57915     {
57916         var s = this.html;
57917      
57918         s = ['<tpl>', s, '</tpl>'].join('');
57919     
57920         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57921             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57922             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57923             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57924             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57925             m,
57926             id     = 0,
57927             tpls   = [];
57928     
57929         while(true == !!(m = s.match(re))){
57930             var forMatch   = m[0].match(nameRe),
57931                 ifMatch   = m[0].match(ifRe),
57932                 execMatch   = m[0].match(execRe),
57933                 namedMatch   = m[0].match(namedRe),
57934                 
57935                 exp  = null, 
57936                 fn   = null,
57937                 exec = null,
57938                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57939                 
57940             if (ifMatch) {
57941                 // if - puts fn into test..
57942                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57943                 if(exp){
57944                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57945                 }
57946             }
57947             
57948             if (execMatch) {
57949                 // exec - calls a function... returns empty if true is  returned.
57950                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57951                 if(exp){
57952                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57953                 }
57954             }
57955             
57956             
57957             if (name) {
57958                 // for = 
57959                 switch(name){
57960                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57961                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57962                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57963                 }
57964             }
57965             var uid = namedMatch ? namedMatch[1] : id;
57966             
57967             
57968             tpls.push({
57969                 id:     namedMatch ? namedMatch[1] : id,
57970                 target: name,
57971                 exec:   exec,
57972                 test:   fn,
57973                 body:   m[1] || ''
57974             });
57975             if (namedMatch) {
57976                 s = s.replace(m[0], '');
57977             } else { 
57978                 s = s.replace(m[0], '{xtpl'+ id + '}');
57979             }
57980             ++id;
57981         }
57982         this.tpls = [];
57983         for(var i = tpls.length-1; i >= 0; --i){
57984             this.compileTpl(tpls[i]);
57985             this.tpls[tpls[i].id] = tpls[i];
57986         }
57987         this.master = tpls[tpls.length-1];
57988         return this;
57989     },
57990     /**
57991      * same as applyTemplate, except it's done to one of the subTemplates
57992      * when using named templates, you can do:
57993      *
57994      * var str = pl.applySubTemplate('your-name', values);
57995      *
57996      * 
57997      * @param {Number} id of the template
57998      * @param {Object} values to apply to template
57999      * @param {Object} parent (normaly the instance of this object)
58000      */
58001     applySubTemplate : function(id, values, parent)
58002     {
58003         
58004         
58005         var t = this.tpls[id];
58006         
58007         
58008         try { 
58009             if(t.test && !t.test.call(this, values, parent)){
58010                 return '';
58011             }
58012         } catch(e) {
58013             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58014             Roo.log(e.toString());
58015             Roo.log(t.test);
58016             return ''
58017         }
58018         try { 
58019             
58020             if(t.exec && t.exec.call(this, values, parent)){
58021                 return '';
58022             }
58023         } catch(e) {
58024             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58025             Roo.log(e.toString());
58026             Roo.log(t.exec);
58027             return ''
58028         }
58029         try {
58030             var vs = t.target ? t.target.call(this, values, parent) : values;
58031             parent = t.target ? values : parent;
58032             if(t.target && vs instanceof Array){
58033                 var buf = [];
58034                 for(var i = 0, len = vs.length; i < len; i++){
58035                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58036                 }
58037                 return buf.join('');
58038             }
58039             return t.compiled.call(this, vs, parent);
58040         } catch (e) {
58041             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58042             Roo.log(e.toString());
58043             Roo.log(t.compiled);
58044             return '';
58045         }
58046     },
58047
58048     compileTpl : function(tpl)
58049     {
58050         var fm = Roo.util.Format;
58051         var useF = this.disableFormats !== true;
58052         var sep = Roo.isGecko ? "+" : ",";
58053         var undef = function(str) {
58054             Roo.log("Property not found :"  + str);
58055             return '';
58056         };
58057         
58058         var fn = function(m, name, format, args)
58059         {
58060             //Roo.log(arguments);
58061             args = args ? args.replace(/\\'/g,"'") : args;
58062             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58063             if (typeof(format) == 'undefined') {
58064                 format= 'htmlEncode';
58065             }
58066             if (format == 'raw' ) {
58067                 format = false;
58068             }
58069             
58070             if(name.substr(0, 4) == 'xtpl'){
58071                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58072             }
58073             
58074             // build an array of options to determine if value is undefined..
58075             
58076             // basically get 'xxxx.yyyy' then do
58077             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58078             //    (function () { Roo.log("Property not found"); return ''; })() :
58079             //    ......
58080             
58081             var udef_ar = [];
58082             var lookfor = '';
58083             Roo.each(name.split('.'), function(st) {
58084                 lookfor += (lookfor.length ? '.': '') + st;
58085                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58086             });
58087             
58088             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58089             
58090             
58091             if(format && useF){
58092                 
58093                 args = args ? ',' + args : "";
58094                  
58095                 if(format.substr(0, 5) != "this."){
58096                     format = "fm." + format + '(';
58097                 }else{
58098                     format = 'this.call("'+ format.substr(5) + '", ';
58099                     args = ", values";
58100                 }
58101                 
58102                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58103             }
58104              
58105             if (args.length) {
58106                 // called with xxyx.yuu:(test,test)
58107                 // change to ()
58108                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58109             }
58110             // raw.. - :raw modifier..
58111             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58112             
58113         };
58114         var body;
58115         // branched to use + in gecko and [].join() in others
58116         if(Roo.isGecko){
58117             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58118                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58119                     "';};};";
58120         }else{
58121             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58122             body.push(tpl.body.replace(/(\r\n|\n)/g,
58123                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58124             body.push("'].join('');};};");
58125             body = body.join('');
58126         }
58127         
58128         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58129        
58130         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58131         eval(body);
58132         
58133         return this;
58134     },
58135
58136     applyTemplate : function(values){
58137         return this.master.compiled.call(this, values, {});
58138         //var s = this.subs;
58139     },
58140
58141     apply : function(){
58142         return this.applyTemplate.apply(this, arguments);
58143     }
58144
58145  });
58146
58147 Roo.XTemplate.from = function(el){
58148     el = Roo.getDom(el);
58149     return new Roo.XTemplate(el.value || el.innerHTML);
58150 };